【NOIP2013】货车运输的做题总结

货车运输,既然是noip题,各大oj上肯定都有.

比如说luogu.

题目是这样的:

A国有n座城市,编号从1到n,城市之间有m条双向道路.每一条道路对车辆都有重量限制(限重).现在有q辆货车在运输货物,司机们想知道每辆车在不超过车辆限制的情况下,最多能运多重的货物.

那么这道题暴力30分就可以直接跑spfa来做,不过爆炸很容易.

60分可以先想一想,我们肯定要让每条道上的限重最大,且题目要求是两点之间的路径,每两点之间走的路径肯定是确定的.

那么我们可以发现什么,路径确定了,又要让限重最大,说明我们肯定是直接在一颗最大生成树上跑.

那么我们在最大生成树上暴力跑就有60分了.

100分的思路也是在最大生成树上跑,但是,我们可以想想,我们可以提前预处理每两个点之间的路径.

提前处理路径,那么每两点u和v之间路径其实就是u到lca(u,v)和v到lca(u,v)这两条路径拼起来.

那么lca我们又可以做到O(log(n))的倍增查询,而倍增刚好又可以顺便记录点到它的祖先的路径中边的最小值,这不就可以用O(log(n))的时间完成一次查询了.

注意,若在kruskal过程中u与v不在一个集合内,那么查询他们两个的时候就直接输出-1.

那么这就是正解.

首先先用kruskal求出最大生成树的树边:

int father[N+1]={0};
bool cmp(side_plan a,side_plan b){
  return a.v>b.v;
}
int get(int u){
  return (u==father[u])? u:get(father[u]);
}
void kruskal(){
  for (int i=1;i<=n;i++)
    father[i]=i;
  sort(e_plan+1,e_plan+m+1,cmp);
  for (int i=1;i<=m;i++)
    if (get(e_plan[i].x)!=get(e_plan[i].y)){
      father[get(e_plan[i].x)]=get(e_plan[i].y);
      ins_tree(e_plan[i].x,e_plan[i].y,e_plan[i].v);
      ins_tree(e_plan[i].y,e_plan[i].x,e_plan[i].v);
    }
}

然后dfs预处理:

int deep[N+1]={0},grand[N+1][20]={0},minn[N+1][20]={0};
bool use[N+1]={0};
void dfs(int k,int dad){
  deep[k]=deep[dad]+1;
  use[k]=1;
  grand[k][0]=dad;
  for (int i=1;grand[k][i-1];i++)
    grand[k][i]=grand[grand[k][i-1]][i-1],minn[k][i]=min(minn[k][i-1],minn[grand[k][i-1]][i-1]);
  for (int i=lin_tree[k];i;i=e_tree[i].next)
    if (!use[e_tree[i].y]) minn[e_tree[i].y][0]=e_tree[i].v,dfs(e_tree[i].y,k);
}

整个查询可以这样写:

int query(int x,int y){
  int mi=INF;
  if (deep[x]<deep[y]) swap(x,y);
  for (int i=19;i>=0;i--)
    if (deep[grand[x][i]]>=deep[y]) mi=min(mi,minn[x][i]),x=grand[x][i];
  for (int i=19;i>=0;i--)
    if (grand[x][i]!=grand[y][i]) mi=min(mi,min(minn[x][i],minn[y][i])),x=grand[x][i],y=grand[y][i];
  if (x!=y) return min(mi,min(minn[x][0],minn[y][0]));
  else return mi;
}
inline void outo(){
  int x,y,q;
  for (int i=1;i<=n;i++)
    for (int j=0;grand[i][j];j++);
  scanf("%d",&q);
  for (int i=1;i<=q;i++){
    scanf("%d%d",&x,&y);
    if (get(x)!=get(y)) printf("-1\n");
    else printf("%d\n",query(x,y));
  }
}

AC代码如下:

#include<bits/stdc++.h>
  using namespace std;
const int M=50000;
const int N=10000;
const int INF=100000000;
int n,m;
struct side_plan{
  int x,y,v;
}e_plan[M+1];
int top_plan=0;
void ins_plan(int X,int Y,int V){
  e_plan[++top_plan].x=X;
  e_plan[top_plan].y=Y;
  e_plan[top_plan].v=V;
}
struct side_tree{
  int y,next,v;
}e_tree[2*N+1];
int top_tree=0,lin_tree[N+1];
void ins_tree(int X,int Y,int V){
  e_tree[++top_tree].y=Y;
  e_tree[top_tree].v=V;
  e_tree[top_tree].next=lin_tree[X];
  lin_tree[X]=top_tree;
}
inline void into(){
  memset(e_plan,0,sizeof(e_plan));
  memset(e_tree,0,sizeof(e_tree));
  scanf("%d%d",&n,&m);
  int x,y,v;
  for (int i=1;i<=m;i++){
    scanf("%d%d%d",&x,&y,&v);
    ins_plan(x,y,v);
  }
}
int father[N+1]={0};
bool cmp(side_plan a,side_plan b){
  return a.v>b.v;
}
int get(int u){
  return (u==father[u])? u:get(father[u]);
}
void kruskal(){
  for (int i=1;i<=n;i++)
    father[i]=i;
  sort(e_plan+1,e_plan+m+1,cmp);
  for (int i=1;i<=m;i++)
    if (get(e_plan[i].x)!=get(e_plan[i].y)){
      father[get(e_plan[i].x)]=get(e_plan[i].y);
      ins_tree(e_plan[i].x,e_plan[i].y,e_plan[i].v);
      ins_tree(e_plan[i].y,e_plan[i].x,e_plan[i].v);
    }
}
int deep[N+1]={0},grand[N+1][20]={0},minn[N+1][20]={0};
bool use[N+1]={0};
void dfs(int k,int dad){
  deep[k]=deep[dad]+1;
  use[k]=1;
  grand[k][0]=dad;
  for (int i=1;grand[k][i-1];i++)
    grand[k][i]=grand[grand[k][i-1]][i-1],minn[k][i]=min(minn[k][i-1],minn[grand[k][i-1]][i-1]);
  for (int i=lin_tree[k];i;i=e_tree[i].next)
    if (!use[e_tree[i].y]) minn[e_tree[i].y][0]=e_tree[i].v,dfs(e_tree[i].y,k);
}
inline void work(){
  kruskal();
  memset(minn,10,sizeof(minn));
  for (int i=1;i<=n;i++)
    if (!use[i]) dfs(i,0);
}
int query(int x,int y){
  int mi=INF;
  if (deep[x]<deep[y]) swap(x,y);
  for (int i=19;i>=0;i--)
    if (deep[grand[x][i]]>=deep[y]) mi=min(mi,minn[x][i]),x=grand[x][i];
  for (int i=19;i>=0;i--)
    if (grand[x][i]!=grand[y][i]) mi=min(mi,min(minn[x][i],minn[y][i])),x=grand[x][i],y=grand[y][i];
  if (x!=y) return min(mi,min(minn[x][0],minn[y][0]));
  else return mi;
}
inline void outo(){
  int x,y,q;
  for (int i=1;i<=n;i++)
    for (int j=0;grand[i][j];j++);
  scanf("%d",&q);
  for (int i=1;i<=q;i++){
    scanf("%d%d",&x,&y);
    if (get(x)!=get(y)) printf("-1\n");
    else printf("%d\n",query(x,y));
  }
}
int main(){
  into();
  work();
  outo();
  return 0;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/80258519