题目描述
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。
输入输出样例
说明
对于 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。
【思路分析】
由于我们这道题只需要知道最大载货量,对于路径的长度没有要求的,所以我们只需要在最大的边上跑就好。那么怎么找最大边,那么就将最小生成树改一下,将边排序时从小到大改为从大到小,变成最大生成树,就可以在保证当前图中的每个点联通并且边权最大,就可以省去跑一些权值小的边的时间,然后就在这个图上跑LCA,跑LCA的时候顺便统计到公共祖先的路径上的最小边,然后输出就好。
值得注意的是这个地方的点可能不是一个连通图,可能有多个,所以要在多个树上跑LCA,不过就是一个for循环的事
最好先看看倍增LCA,只要会倍增LCA,那么这道题写出代码还是很快的
具体看代码!
【代码实现】
1 #include<cstdio> 2 #include<vector> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 using namespace std; 7 const int MAXN=1e6+7; 8 struct sd{ 9 int ww,id;//记录到哪个节点,路径权值是多少 10 }jump[10005][21],fa[10005];//用于跑LCA的结构体 11 struct sd1{ 12 int next,to,w; 13 }edge[50005];//用于最大生成树的结构体 14 int ff[10005];//并查集 15 int deep[10005]; 16 bool vis[10005]; 17 vector<sd> son[10005];//记录跑LCA的树 18 vector<sd> link[10005];//记录之前的无向图 19 int n,m,q; 20 void find(int v)//倍增LCA 21 { 22 for(int i=1;i<=20;i++) 23 { 24 int temp=jump[v][i-1].id; 25 jump[v][i].id=jump[temp][i-1].id; 26 jump[v][i].ww=min(jump[v][i-1].ww,jump[temp][i-1].ww); 27 } 28 } 29 void pre(int v)//LCA的预处理 30 { 31 deep[v]=deep[fa[v].id]+1; 32 jump[v][0].id=fa[v].id; 33 jump[v][0].ww=fa[v].ww; 34 find(v); 35 int ss=son[v].size(); 36 if(ss==0) 37 return; 38 for(int i=0;i<ss;i++) 39 pre(son[v][i].id); 40 } 41 int lca(int a,int b)//跑LCA 42 { 43 if(deep[b]>deep[a]) {int t=a;a=b;b=t;} 44 int len=deep[a]-deep[b]; 45 int ans=MAXN; 46 if(len!=0) 47 { 48 for(int i=20;i>=0;i--) 49 { 50 if(deep[jump[a][i].id]>=deep[b]) 51 { 52 ans=min(ans,jump[a][i].ww); 53 a=jump[a][i].id; 54 } 55 } 56 } 57 if(a==b) return ans;//如果两个节点属于祖先与后代关系,就不用继续了 58 for(int i=20;i>=0;i--) 59 { 60 if(jump[a][i].id!=jump[b][i].id) 61 { 62 ans=min(jump[a][i].ww,ans); 63 ans=min(jump[b][i].ww,ans); 64 a=jump[a][i].id,b=jump[b][i].id; 65 } 66 } 67 ans=min(ans,fa[a].ww); 68 ans=min(ans,fa[b].ww); 69 return ans; 70 } 71 bool cmp(sd1 a,sd1 b) 72 { 73 return a.w>b.w; 74 } 75 int father(int v) 76 { 77 if(ff[v]!=v) return ff[v]=father(ff[v]); 78 return v; 79 } 80 void dfs(int v)//预处理建立跑LCA的图 81 { 82 vis[v]=true; 83 int ss=link[v].size(); 84 for(int i=0;i<ss;i++) 85 { 86 if(vis[link[v][i].id]==false) 87 { 88 son[v].push_back(link[v][i]); 89 fa[link[v][i].id].id=v; 90 fa[link[v][i].id].ww=link[v][i].ww; 91 dfs(link[v][i].id); 92 } 93 } 94 } 95 void kls()//克鲁斯卡尔预处理 96 { 97 sort(edge+1,edge+1+m,cmp); 98 for(int i=1;i<=n;i++) ff[i]=i; 99 for(int i=1;i<=m;i++) 100 { 101 int f1=father(edge[i].next); 102 int f2=father(edge[i].to); 103 if(f1!=f2) 104 { 105 ff[f1]=f2; 106 sd gg1,gg2; 107 gg1.id=edge[i].to,gg1.ww=edge[i].w; 108 gg2.id=edge[i].next,gg2.ww=edge[i].w; 109 link[edge[i].next].push_back(gg1); 110 link[edge[i].to].push_back(gg2); 111 } 112 } 113 } 114 int main() 115 { 116 memset(vis,false,sizeof(vis)); 117 scanf("%d%d",&n,&m); 118 for(int i=1;i<=m;i++) 119 { 120 int x,y,w1; 121 scanf("%d%d%d",&x,&y,&w1); 122 edge[i].next=x,edge[i].to=y,edge[i].w=w1; 123 } 124 kls(); 125 for(int i=1;i<=n;i++)//循环建立树,防止不止一个联通图 126 { 127 if(vis[i]) continue; 128 deep[i]=1; 129 dfs(i); 130 pre(i); 131 } 132 scanf("%d",&q); 133 for(int i=1;i<=q;i++) 134 { 135 int a,b; 136 scanf("%d%d",&a,&b); 137 if(father(a)!=father(b))//如果不在一个联通图上,那么肯定不能相互到达 138 { 139 printf("-1 "); 140 continue; 141 } 142 printf("%d ",lca(a,b)); 143 } 144 return 0; 145 }