Avito Cool Challenge 2018:D. Maximum Distance (最小生成树)
题意 :
给出一个联通图和一些特殊的点,现在定义cost(u,v)为一条从u到v的路径上面边权的最大值 ,
定义dis(u,v) 为从u到v 路径上面cost 的最小值
然后求所有特殊点到其他特殊点的最大距离
题解:
做这题前,首先思考一件事情,对于一颗树来说点到点的距离是不是就是树上面路径的边权最大值
我们来证明一下:假设在最小生成树上面的路径cost为w1,另外在原图中还有一条路径从u到v,其cost为w2,那么必然有w2>w1的。那么我们最后的dis一定是w1。
那么我们现在的目标就是求特殊点到特殊点之间的最大距离。注意一下这里是从一个特殊点到其它所有特殊点的最大距离
我们根据Kruskal 算法的构建过程 , 在构建树的时候是先构造小的边的 , 所以我们就可以在Kruskal加边的时候更新答案 ,
我们假设现在有两个集合,现在将其连接起来,当满足两个集合里面都有特殊点时我们就可以更新答案了,否则就不行。
转载 现在还有一些问题没有解决 , 待后跟新
#include<bits/stdc++.h> using namespace std; const int maxn = 100001;//最大点数 int c[maxn], N,M,k;//并查集使用 int cnt; bool a[maxn]; int VAL[maxn]; struct EDGE{ int from, to, w; bool operator < (const EDGE &rhs) const{ return this->w < rhs.w; }; }Edge[maxn];//储存边的信息,包括起点/终点/权值 inline void init() { for(int i=0; i<=N; i++) c[i] = i; cnt = 0; } inline void AddEdge(int from, int to, int weight) { Edge[cnt].from = from; Edge[cnt].to = to; Edge[cnt].w = weight; cnt++; } int Findset(int x) { int root = x; while(c[root] != root) root = c[root]; int idx; while(c[x] != root){ /// 路径压缩 idx = c[x]; c[x] = root; x = idx; } return root; } int Kruskal()//传入点数,返回最小生成树的权值,如果不连通返回-1 { sort(Edge,Edge+cnt); int EdgeCnt=0;//计算加入的边数 int Cost=0; int MAX=0; for(int i=0;i<cnt;i++){ int u=Edge[i].from; int v=Edge[i].to; int w=Edge[i].w; int R1 = Findset(u); int R2 = Findset(v); if(R1==R2) continue; c[R1]=R2; if(a[u]) VAL[R1]++;//标记的点 if(a[v]) VAL[R2]++; if(VAL[R1] && VAL[R2] )//如果标记的点都有 MAX=w; VAL[R2]+=VAL[R1]; EdgeCnt++; if(EdgeCnt==N-1) break; } if(EdgeCnt<N-1) return -1;//不连通 else return MAX; } int main() { scanf("%d%d%d",&N,&M,&k); init(); int Val; for(int i=1 ; i<=k ; i++) { scanf("%d",&Val); a[Val]=1; } for(int i=1 ; i<=M ; i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); AddEdge(u,v,w); } int P=Kruskal(); for(int i=1 ; i<=k ; i++) printf("%d ",P); // printf("%d\n", Kruskal()); return 0; }