Boruvka

大概是这样的:一开始图中有\(n\)个连通块,每次操作我们选出各个连通块连出去的最短的边(如果有相同边权的边的话可以把序号作为第二关键字),然后把这些边加入最小生成树。
最坏的情况下每次操作都会让当前的连通块减半,因此Boruvka算法的复杂度为\(O(m\log n)\)
主要的应用在于边数为\(O(n^2)\)级别但是有特殊性质(如曼哈顿距离、异或等)的情况,选各个连通块连出去的边的时候可以不枚举每条边而是直接找。

#include<bits/stdc++.h>
const int N=5007,M=200007;
struct edge{int u,v,w;}e[M];
int vis[M],a[N],fa[N];
int read(){int x;scanf("%d",&x);return x;}
int cmp(int i,int j){return !j? 1:(e[i].w<e[j].w||(e[i].w==e[j].w&&i<j));}
int Find(int x){return x==fa[x]? x:fa[x]=Find(fa[x]);}
int main()
{
    int n=read(),m=read(),i,cnt=1,sum=0,f=1,u,v;
    for(i=1;i<=m;++i) e[i]=(edge){read(),read(),read()};
    for(i=1;i<=n;++i) fa[i]=i;
    while(f)
    {
	f=0,memset(a,0,sizeof a);
        for(i=1;i<=m;++i)
	{
	    if(vis[i]||(u=Find(e[i].u))==(v=Find(e[i].v))) continue;
	    if(cmp(i,a[u])) a[u]=i;
	    if(cmp(i,a[v])) a[v]=i;
        }
        for(i=1;i<=n;++i)
            if(a[i]&&!vis[a[i]])
		f=1,++cnt,sum+=e[a[i]].w,vis[a[i]]=1,fa[Find(e[a[i]].u)]=Find(e[a[i]].v);
    }
    cnt==n? printf("%d",sum):puts("orz");
}
posted @ 2019-11-10 10:19  Shiina_Mashiro  阅读(160)  评论(0编辑  收藏  举报