Java教程

最小生成树

本文主要是介绍最小生成树,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

专门开个博客一是因为没地放了,二是以后次小生成树什么的就一块扔这了。

点数n,边数m的图的最小生成树大概有两个算法:

  1. Kruskal算法(\(O(m\log m)\))

思路非常简单粗暴,把所有边扔出来按照边权排个序,然后拿并查集维护点的连通关系,最后选出n-1条边。

int kruskal(int x){
    sort(edge+1,edge+t+1,cmp);
    chushihua();
    int sum=0,cnt=0;
    for(int i=1;i<=t;i++){
        int x=edge[i].u,y=edge[i].v;
        if(find(x)!=find(y)){
            father[y]=x;
            sum+=edge[i].w;
            cnt++;
        }
        if(cnt==n-1)break;
    }
    return sum;
}
  1. Prim算法(\(O(n^2)\))

这个是每次维护最小生成树的一部分边,类似Dijkstra。(这玩意真没啥意思)

同样的,用堆优化可以到\(O(mlogn)\),但是你还不如直接写kruskal。

int prim(int x){
    int sum=0;memset(a,0x3f,sizeof(a));
    for(int i=1;i<=n;i++)dis[i]=a[x][i];
    v[x]=true;
    for(int i=2;i<=n;i++){
        int min=2147483647,k;
        for(int j=1;j<=n;j++){
            if(!v[j]&&dis[j]<min)min=dis[j];k=j;
        }
        sum+=dis[k];v[k]=true;//找一条与当前生成树相连的最小的边记录答案 
        for(int j=1;j<=n;j++){
            if(!v[j]&&dis[j]>a[k][j])dis[j]=a[k][j];//更新边 
        }
    }
    return sum;
}
  1. Boruvka算法(听完林老师讲课看看原题题解看到的)(\(O(m\log n)\))

首先定义最小边是一个连通块向其他连通块连的边中边权最小的一个。这个算法的大体思路是初始将每个点视作一个连通块,通过最小边合并连通块(共\(\log n\)次合并),最终形成最小生成树。这个东西一般不会让你写,但是不太好建边的时候可能有奇效(比如这道题)。

int link[5010],val[5010];
void boruvka(int n){
	int ans=0,num=0;
	bool jud=true;
	while(jud){
		jud=false;
		memset(link,0,sizeof(link));
		memset(val,0x3f,sizeof(val));
		for(int i=1;i<=n;i++){
			int x=find(i);
			for(int j=head[i];j;j=edge[j].next){
				int y=find(edge[j].v);
				if(val[x]>edge[j].w&&x!=y){//找到连通块i的最小边 
					val[x]=edge[j].w;//如果该边两端点不都属于连通块且边权更小则更新 
					link[x]=y;
				}
			}
		}
		for(int i=1;i<=n;i++){
			int x=find(i);
			if(find(i)==i){
				if(link[x]&&x!=find(link[x])){
					merge(x,link[x]);ans+=val[x];//连接最小边两端的两个连通块 
					jud=true;num++;//计入合并次数(洛谷的无解要特判一下) 
				}
			}
		}
	}
	if(num==n-1)printf("%d",ans);
	else printf("orz");
}
这篇关于最小生成树的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!