图论——最小生成树Kruskal算法模板

 Kruskal算法

   1、算法思想:所有边从小到大依次排序(sort、qsort);依次考查每条边(u,v),有两种情况:

     情况1:u和v在同一个连通分量中,那么加入(u,v)后会形成环,因此不能选。

     情况2:如果不在一个连通分量,那么加入(u,v)一定是最优解,证明反证法:不加(u,v)存在最小生成树的话,那么加上(u,v)一定有且只有一个环,环中至少有一条边(u’,v)的权值大于(u,v)的权值。那么删除该边后,得到的新树T’=T+(u,v)-(u’,v)<= T。所以(u,v)是最优解。(很绕可以略过证明)

 ?问题1—— 判断任意两个点是否在同一个连通分量,还需要合并两个连通分量。

 办法1:暴力 (dfs,bfs),算法效率低

 办法2:并查集(Union-Find Set)

递归思想:把 X 的父节点保存在P[X]中(如果x没有父节点,则P[X])弊端:假如这棵树是长长的一条链,每次查找就会很慢。

可以用压缩路径来优化。

2、例题:hdu_1863畅通工程


 以hdu_1863畅通工程为例,Kruskal算法适合 稀疏图
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define N 105
int n,m;//n条边,m个结点 
struct road{
	int xi,yi,vi;
}rd[N];
int par[N];//双亲结点 
int cmp(road x,road y){
	return x.vi<y.vi ;
}
int Unfind(int x){//并查集_查找,压缩路径 
	if(!par[x])return x;
	int i=x;
	while(par[i]) 
		i=par[i];
	int j=x,temp;
	while(par[j]){
		temp=par[j];
		par[j]=i;
		j=temp;
	}
	return i;
}
int main(){
	while(~scanf("%d%d",&m,&n)&&n!=0){
		memset(rd,0,sizeof(rd));//初始化结构体 
		memset(par,0,sizeof(par));//初始化双亲结点 
		for(int i=0;i<n;i++){
			scanf("%d%d%d",&rd[i].xi,&rd[i].yi,&rd[i].vi);
		}
		sort(rd,rd+n,cmp);
		int res=m,ans=0;
		for(int i=0;i<n&&res>1;i++){
			int nx=Unfind(rd[i].xi);
			int ny=Unfind(rd[i].yi);
			if(nx!=ny){//不是同一个连通分量合并 
				res--;
				printf("**%d\n",rd[i].vi);
				ans+=rd[i].vi;
				par[nx]=ny;
			}
		}
		if(res==1)//判断是否能生成树,边数等于结点数减 1 
			printf("%d\n",ans);
		else
			printf("?\n");
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值