3.货车运输
(truck.cpp/c/pas)
【问题描述】
A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
【输入】
输入文件名为 truck.in。
输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。
接下来 m 行每行 3 个整数 x、y、z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意:x 不等于 y,两座城市之间可能有多条道路。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市 运输货物到 y 城市,注意:x 不等于 y。
【输出】
输出文件名为 truck.out。
输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。
【输入输出样例】
truck.in |
truck.out |
||||
4 |
3 |
3 |
|||
1 |
2 |
4 |
-1 |
||
2 |
3 |
3 |
3 |
||
3 |
1 |
1 |
|||
3 |
|||||
1 |
3 |
||||
1 |
4 |
||||
1 |
3 |
||||
【数据说明】
对于 30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q< 1,000;
对于 60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q< 1,000;
对于 100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q< 30,000,0 ≤ z ≤ 100,000。
【思路】
MST+LCA
可以知道如果运最大重量的货物,货车所走的一定是最大生成树上的边。
方法:kruskal求解最大生成树。
对于每个询问uv,找到两者的最近公共祖先r,那么货车走过的路线就是u->r->v,所以只需要在寻找LCA的时候比较路上的最小边即可。
方法:暴力。先将uv挪到相同深度上来,然后并行向上寻找公共祖先。
注意:当uv不属于同一个树中的时候意味着两者不互通,需要提前判断。
【代码】
1 #include<iostream> 2 #include<vector> 3 #include<cstring> 4 #include<algorithm> 5 #define FOR(a,b,c) for(int a=(b);a<(c);a++) 6 using namespace std; 7 8 const int maxn = 10000+10, maxm=50000+10; 9 int n,m; 10 int father[maxn]; //并查集 11 struct Edge{ 12 int u,v,d; 13 bool operator <(const Edge& rhs) const{ //最大生成树 14 return d>rhs.d; 15 } 16 }; 17 struct Edge2{ 18 int v,d,next; 19 }; 20 inline int find(int u) { 21 return u==father[u]? u:father[u]=find(father[u]); 22 } 23 struct LCA{ 24 int first[maxm]; //链表式存初图 25 Edge2 e[maxm]; int en; 26 27 int dist[maxn][maxn]; //[][] 28 int d[maxn]; //深度数组 29 int p[maxn]; //p数组记录父节点 30 int root; 31 32 void init() { 33 en=0; root=n/2; //root的选取会影响时间 34 for(int i=0;i<n;i++) first[i]=-1; 35 d[0]=0; 36 } 37 void AddEdge(int u,int v,int d){ 38 ++en; 39 e[en].v=v; e[en].d=d; 40 e[en].next=first[u]; 41 first[u]=en; 42 } 43 void build_tree(int u,int fa) { //have failed 44 //根据MST得出的初图(edges)建树->depth[] parent[] dist[][] 45 //无根树->有根树 46 for(int i=first[u];i>0;i=e[i].next) { //i=next[i]!!! 47 int v=e[i].v; 48 if(v!=fa) { 49 d[v]=d[u]+1; dist[u][v]=dist[v][u]=e[i].d; 50 build_tree(v,p[v]=u); //v!=fa 51 } 52 } 53 } 54 void Move(int& u,int depth,int &ans){ //u溯回到高度为depth的祖先的位置 55 while(d[u]!=depth) { ans=min(ans,dist[u][p[u]]); u=p[u]; } 56 } 57 int query(int u,int v) { 58 if(find(u) != find(v)) return -1; //uv之间不可达 59 int ans=1<<30; 60 if(d[u]<d[v]) Move(v,d[u],ans); else if(d[u]>d[v]) Move(u,d[v],ans); 61 while(u != v) { 62 ans=min( ans , min(dist[u][p[u]],dist[v][p[v]]) ); 63 u=p[u]; v=p[v]; 64 } 65 return ans; 66 } 67 }; 68 LCA lca; 69 struct Kruskal{ 70 vector<Edge> edges; 71 72 void init() { 73 edges.clear(); 74 for(int i=0;i<n;i++) father[i]=i; 75 } 76 void AddEdge(int u,int v,int d) { 77 edges.push_back((Edge){u,v,d}); 78 } 79 void MST() { 80 lca.init(); 81 sort(edges.begin(),edges.end()); 82 int cnt=0; 83 int nc=edges.size(); 84 for(int i=0;i<nc;i++) { 85 int u=edges[i].u,v=edges[i].v,d=edges[i].d; 86 int x=find(u),y=find(v); 87 if(x!=y) { 88 lca.AddEdge(u,v,d); //利用MST中的边构造lca 89 lca.AddEdge(v,u,d); //反向边 90 father[x]=y; 91 if(++cnt==n-1) return ; //判断有n-1条边提前结束 92 } 93 } 94 } 95 }; 96 97 Kruskal krus; 98 99 int main() { 100 ios::sync_with_stdio(false); 101 cin>>n>>m; 102 krus.init(); 103 FOR(i,0,m) { //uv 0.. 104 int u,v,d; 105 cin>>u>>v>>d; u--; v--; 106 krus.AddEdge(u,v,d); 107 } 108 krus.MST(); 109 lca.build_tree(lca.root,-1); 110 int q; cin>>q; 111 FOR(i,0,q) { 112 int u,v; cin>>u>>v; u--; v--; 113 cout<<lca.query(u,v)<<" "; 114 } 115 return 0; 116 }