2023年--Brouvka算法
SOl
1:认为每个点最开始时都是一个点集
2:不断进行更新操作-----进行点集的合并
如何合并呢?
本质则个贪心算法,即每个点集都会找出一条自己“伸”出去的最短边与别人合并
当然对于某条边,可能同时是A集合伸出去的最短边,也是B集合伸出去的。
例如下图的右边,标箭头的就是这个点集伸出去的最短边
3:找出最短边后,枚举点集,是枚举点集哟。
针对这个点集伸出去的边,如果存在,并且没在MST中的话
则这条边的左右端点所代表的点集,就能合并。
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+50,maxm=2e5+5; const int MaxN = 5000 + 5, MaxM = 200000 + 5; int N, M; int U[MaxM], V[MaxM], W[MaxM],Best[maxn]; bool used[MaxM]; int father[maxn]; void init(int n) { for(int i=1;i<=n;i++) father[i]=i; } int find(int x){return x==father[x]?x:father[x]=find(father[x]);} void unionSet(int x,int y) { int xx=find(x),yy=find(y); if(xx==yy) return; father[xx]=yy; } inline bool Better(int x, int y) { if (y == 0) return true;
//y代表某个点集所伸出去的那条边的编号,如果为0,则说明当前的第x条就是这个点集伸出去的边
//否则,则说明这个点集有多条边伸出去,于是取其是最短的。 if (W[x] != W[y]) return W[x] < W[y]; return x < y; } void Boruvka() { init(N); int merged = 0, sum = 0; bool update = true; while (update) { update = false; memset(Best, 0, sizeof Best); //将每个点集清空 for (int i = 1; i <= M; ++i) //枚举所有的边 { if (used[i] == true) //如果这个边已在MST中,就不管 continue; int p = find(U[i]), q = find(V[i]); //看边的两个点,分别在哪个集合 if (p == q) continue; if (Better(i, Best[p]) == true) //看下第i条边的长度,是否比p这个集合伸出来的边,要更优 Best[p] = i; if (Better(i, Best[q]) == true) Best[q] = i; } for (int i = 1; i <= N; ++i) //枚举所有的点集 if (Best[i] != 0 && used[Best[i]] == false) //如果这个点集有边伸出来了,并且这条边没在MST中 { update = true; //说明有更新操作 merged++; //加了多少条边 sum += W[Best[i]]; //代价加起来 used[Best[i]] = true; //这个边在MST中 unionSet(U[Best[i]],V[Best[i]]); //把这两个集合合并 } } if (merged == N - 1) printf("%d\n", sum); else puts("orz"); } int main() { scanf("%d %d", &N, &M); for (int i = 1; i <= M; ++i) scanf("%d%d%d", &U[i], &V[i], &W[i]); Boruvka(); return 0; }
https://www.cnblogs.com/glq-Blog/p/16581535.html
https://www.zsbeike.com/technology/8e75f8083e.html
https://www.luogu.com.cn/blog/ETHANK/boruvka-xiao-ji