Kruskal算法最小生成树(MST)

问题:畅通工程(06年浙江大学研究生上机题目)

问题描述:
某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。
 
输入:
测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。
当N为0时,输入结束,该用例不被处理。
 
输出:
对每个测试用例,在1行里输出最小的公路总长度。
 
样例输入:
3
1 2 1
1 3 2
2 3 4
4
1 2 1
1 3 4
1 4 1
2 3 3
2 4 2
3 4 5
0
 
样例输出:
3

5

kruskal算法思想:

先构造一个只含 n 个顶点、而边集为空的子图,把子图中各个顶点看成各棵树上的根结点,之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,即把两棵树合成一棵树,反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直到森林中只有一棵树,也即子图中含有 n-1 条边为止。(摘自百度百科~~)

并查集简介:

并查集就是树的双亲存储结构,可以简单的用一个数组表示,数组下表表示结点的编号,数组值存储该结点双亲结点的编号,并且可以把根结点的双亲设置为-1等特殊值,以方便操作。

int vset[N];									//定义一个并查集

int findRoot(int x)								//在并查集中找到根结点,并进行路径压缩
{
	if(vset[x]==-1) return x;
	else
	{
		int root = findRoot(vset[x]);
		vset[x]=root;
		return root;
	}
}

		for (int i = 1; i < N; i++)					//初始化并查集
			vset[i]=-1;

初始化并查集,开始认为每个结点均自己构成一个集合,不和其他结点为伍~~~

问题分析:

简单的说就是,将各边按权值进行递增排序,然后由小到大选出还未在最小生成树中的边,将边的权值累加,直至所有结点都加入最小生成树(怎样防止将边或者结点重复加入生成树,这里就用到并查集)

看代码吧:

#include<stdio.h>
#include<algorithm>

using namespace std;

#define N 4951

struct Road										//边的结构体
{
	int a,b;
	int weight;
	bool operator < (const Road & A) const{		//重载小于运算符,用于排序
		return weight < A.weight;
	}
};

int vset[N];									//定义一个并查集

int findRoot(int x)								//在并查集中找到根结点,并进行路径压缩
{
	if(vset[x]==-1) return x;
	else
	{
		int root = findRoot(vset[x]);
		vset[x]=root;
		return root;
	}
}

int main()
{
	int n,a,b,w;
	Road road[N];
	int root_1,root_2;
	int roadLength;
	int tmp;
	while (scanf("%d",&n)!=EOF&& n!=0)
	{
		tmp=n*(n-1)/2;
		/*一下是kruskal算法精髓*/
		for (int i = 1; i < N; i++)					//初始化并查集
			vset[i]=-1;

		for (int i = 1; i <=tmp; i++)				//输入边两头结点编号及边权值
		{
			scanf("%d%d%d",&a,&b,&w);
			road[i].a=a;road[i].b=b;road[i].weight=w;
		}
		sort(road+1,road+tmp+1);					//对边按权值又小到大进行排序,注意这里使用了STL
		roadLength=0;
		for (int i = 1; i <= tmp; i++)
		{
			root_1 = findRoot(road[i].a);
			root_2 = findRoot(road[i].b);
			if(root_1!=root_2)
			{
				vset[root_2]=root_1;
				roadLength+=road[i].weight;			//累加最小公路长度
			}
		}
		printf("%d\n",roadLength);					//输出
	}
	return 0;
}

黑框运行结果:


后记:

本例还用到了c++的STL中的sort函数,用法为sort(起点地址,结束地址);且sort默认递增排序









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值