Boruvka求最小生成树
我丢:luogu P3366 【模板】最小生成树
相信大家都会prim和kruskal
这篇主要讲Boruvka求最小生成树
算法大致过程就是
对于每个连通块,每次找出一条最小的出边
然后把两个连通块合并
每次可以是连通块个数减半
然后就得到了一个
O
(
(
n
+
m
)
l
o
g
n
)
O((n+m)log n)
O((n+m)logn)的做法
如果用优先队列优化可能可以更加优秀
#include<bits/stdc++.h>
#define N 1000005
using namespace std;
struct edge{
int v, c, nxt;
}e[N], mi[N];
int p[N], eid;
void init(){
memset(p, -1, sizeof p);
eid = 0;
}
void insert(int u, int v, int c){
e[eid].v = v;
e[eid].c = c;
e[eid].nxt = p[u];
p[u] = eid ++;
}
int n, m, fa[N];
int get(int x){//并查集维护连通块
return (fa[x] == x)? x:(fa[x] = get(fa[x]));
}
void merge(int x, int y){
x = get(x), y = get(y);
fa[y] = x;
}
int main(){init();
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++) fa[i] = i;
for(int i = 1; i <= m; i ++){
int u, v, c;
scanf("%d%d%d", &u, &v, &c);
insert(u, v, c);
insert(v, u, c);
}
int ans = 0 , edg = 0;
while(1){
memset(mi, 0x3f, sizeof mi);
for(int i = 1; i <= n; i ++){
for(int j = p[i]; j + 1; j = e[j].nxt){
int v = e[j].v, c = e[j].c;
if(get(v) != get(i) && c < mi[get(i)].c){//对于每个连通块找一个最小的出边
mi[get(i)].c = c, mi[get(i)].v = v;
}
}
}
int F = 0;
for(int i = 1; i <= n; i ++){
int v = mi[i].v, c = mi[i].c;
if(mi[i].v != mi[0].v && get(i) != get(v)) merge(i, v), ans += c, F = 1, edg ++;//合并连通块
}
if(!F) break;
}
if(edg != n - 1) printf("orz");//输出
else printf("%d", ans);
return 0;
}
个人感觉比kruskal的可扩展性强很多
还是一个很有用的算法