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的可扩展性强很多
还是一个很有用的算法

posted @ 2019-08-20 12:47  lahlah  阅读(31)  评论(0编辑  收藏  举报