Borůvka MST算法
当我认为最MST(最小生成树)已经没有什么学的了,才发现世界上还有个这个kruskal和prim结合的玩意
Borůvka 运用并查集的思想,先将每一个初始点集初始化为有且只有自己的点集,然后每一次合并都从所有的独立集合出发,找到一条权值最小的(权值相同,则编号最小)连向其他集合的边,然后合并集合,很容易看出,每次合并都会使集合总数少一半,合并次数大致为log n
相比之下,Borůvka的合并次数要远远少于其他两个常用的MST算法,但是,毕竟不是一般情况下最快的,但是在求解一些个别的(例如边数多,点数较少的完全图)问题有奇效
luogu MST的例题,过不去,最后一个点会T,毕竟Borůvka是用来解决完全图的,并不是一般情况下最快的
1 #include "bits/stdc++.h" 2 using namespace std; 3 #define LL long long 4 const int N = 2e5 + 10; 5 int n,m; 6 int s[N],t[N],w[N]; 7 struct Boruvka 8 { 9 int f[N],use_edge[N],best[N]; 10 void init() 11 { 12 for(int i = 1;i <= n;i ++) f[i] = i,best[i] = 0; 13 for(int i = 1;i <= m;i ++) use_edge[i] = 0; 14 } 15 int find(int x) 16 { 17 return f[x] == x ? x : (f[x] = find(f[x])); 18 } 19 int better(int x,int y) 20 { 21 if(!y) return 1; 22 if(w[x] < w[y]) return 1; 23 return x < y; 24 } 25 LL get(void) 26 { 27 int merge=0; 28 LL sum=0; 29 while(merge != n - 1) 30 { 31 for(int i = 1;i <= m;i ++) 32 { 33 if(use_edge[i]) continue; 34 int x = find(s[i]),y = find(t[i]); 35 if(x == y) continue; 36 if(better(i,best[x])) best[x] = i; 37 if(better(i,best[y])) best[y] = i; 38 } 39 for(int i = 1;i <= n;i ++) 40 { 41 if(best[i]){ 42 int x = find(s[best[i]]),y = find(t[best[i]]); 43 if(x != y){ 44 use_edge[best[i]] = 1; 45 f[x] = y; 46 merge ++; 47 sum += w[best[i]]; 48 } 49 best[i] = 0; 50 } 51 } 52 } 53 return sum; 54 } 55 }g; 56 int main(void) 57 { 58 ios::sync_with_stdio(false); 59 cin >> n >> m; 60 for(int i = 1;i <= m;i ++) cin >> s[i] >> t[i] >> w[i]; 61 g.init(); 62 cout << g.get() << '\n'; 63 return 0; 64 }
很经典的题,题目大意为:
给定一个n个节点的完全图,每个节点有个编号ai,节点i和节点j之间边的权值为ai xor aj,求该图的最小生成树的权值和。
完全图,优先考虑Borůvka
需要在n log n左右的时间中求出每个连通块最小的连接的边,在这道题中边权可以通过点权求出
但是,这是不能直接算出每条边的边权的,因为这一看就是会超时的
所以根据二进制的性质,考虑建对全局建一个01trie,然后再给每一个连通块建一个tire,Borůvka算法每一次合并两个连通块,就合并两个tire,再在trie上维护子树的size,点权异或最小值就直接在全局trie与当前连通块的size作差得到树上贪心即可
总共会迭代log n次,总时间复杂度为O(n logn logsize ),空间复杂度为O(n log size)
code,仅供参考,没有注释
1 #include"bits/stdc++.h" 2 #define ll long long 3 #define inl inline 4 #define reg register 5 #define ls ch[now][0] 6 #define rs ch[now][1] 7 using namespace std; 8 const int N = 2e5 + 10; 9 const int inf = 0x7ffffff; 10 11 int L[N * 40],R[N * 40]; 12 int ch[N * 40][2]; 13 int tot; 14 int n,a[N],root; 15 inl int read(void) 16 { 17 int x = 0,f = 1;char ch = getchar(); 18 while(!isdigit(ch)) f = ch == '-' ? - 1 : f,ch = getchar(); 19 while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0',ch = getchar(); 20 return x * f; 21 } 22 inl void Insert(int &now,int x,int dep) 23 { 24 if(!now) now = ++ tot; 25 L[now] = min(L[now],x),R[now] = max(R[now],x); 26 if(dep < 0) return; 27 int bit = a[x] >> dep & 1; 28 Insert(ch[now][bit],x,dep - 1); 29 } 30 inl int query(int now,int val,int dep) 31 { 32 if(dep < 0) return 0; 33 int bit = val >> dep & 1; 34 if(ch[now][bit]) return query(ch[now][bit],val,dep - 1); 35 else return query(ch[now][bit ^ 1],val,dep - 1) + (1 << dep); 36 } 37 inl ll dfs(int now,int dep) 38 { 39 if(dep < 0) return 0; 40 if(R[ls] && R[rs]) 41 { 42 int minn = inf; 43 for(int i = L[ls];i <= R[ls];i ++) minn = min(minn,query(rs,a[i],dep - 1)); 44 return dfs(ls,dep - 1) + dfs(rs,dep - 1)+ minn + (1 << dep); 45 } 46 if(R[ls]) return dfs(ls,dep - 1); 47 if(R[rs]) return dfs(rs,dep - 1); 48 return 0; 49 } 50 int main(void) 51 { 52 n = read(); 53 for(int i = 1;i <= n;i ++) a[i] = read(); 54 sort(a + 1,a + 1 + n); 55 memset(L,0x3f,sizeof(L)); 56 for(int i = 1;i <= n;i ++) Insert(root,i,30); 57 printf("%lld\n",dfs(root,30)); 58 return 0; 59 }
本文来自博客园,作者:Ech0_7,转载请注明原文链接:https://www.cnblogs.com/empty-space/p/17946015