Kruskal算法的应用

寡人的难题

/*
★实验任务
寡人心系天下为国为民,想要在历史中留下点痕迹,就必须要让国家强盛起来,正所谓
想致富先修路,寡人觉得去修路,那些吃干饭的大臣给了寡人很多条要修的道路,奈何国库
空虚,寡人只能选择其中一些道路,把重点城市连接在一起,并且这些道路的花费要最少,
寡人决定让你来接受这个任务,替寡人分忧。

★数据输入
第一行有两个正整数 n,m,
表示有 n 个城市(城市按照 1 到 n 编号),m 条道路可选择,
接下来有 m 行,每行有三个正整数 u,v,c,
分别表示这一条道路连通 u 和 v 且花费黄金 c 两。
(1<=n<=50000,n-1<=m<=200000,0<=c<=10000)

★数据输出
输出能连通所有城市的道路的最小花费。
输入示例
3 3
1 2 3
2 3 4
1 3 2
输出示例
5

输入示例
5 6
1 2 2
1 5 1
2 5 1
2 3 3
3 4 4
4 5 5
输出示例
9
*/

解题思路

一、采用并查集结合Kruskal算法。其中并查集的应用见“并查集的应用”

二、先做准备工作,新建一个结构体Road,储存道路信息,u,v代表一条路上的两个城市,w代表这条路的花费。
定义cmp函数,用于后面的快排,按照道路花费从小到大排
定义find函数,用于查找根节点
定义Union函数,用于合并两个不同的树,都是将元素少的合并到元素多的,保持树的扁平化,利于查找

三、Kruskal算法
建立一个含n个顶点的图,图中不含边,把这n个顶点看成是各个树的根节点
然后将所有道路按从小到大排序
取出花费最少的道路,如果这两个城市未连通,就把他们连通起来,即这两个顶点不在同一棵树上,就把他们合并成一棵树
反之,若在一棵树上,则跳过该道路,取下一条花费最少的道路
以此类推,直到森林中只有一棵树,即有n-1条道路时停止操作
最小生成树不唯一,但最小花费是唯一的

找了一张示意图如下,有助于理解
Kruskal流程示意图
剩余操作见代码

#include <bits/stdc++.h>
#include <algorithm>
#define Max 200001
using namespace std;
int size[Max];//size[i]表示i所在的树总共有多少个节点 
int id[Max];//id[i]的值表示i的父亲节点 

struct Road 
{
	int u,v,w; 
} r[Max];

bool  cmp(const Road &x, const Road &y) 
{ 
    return x.w<y.w;//按照每条路上的花费从小到大排
}

int find(int x)//查询x的根节点 
{
	while(x!=id[x])
	{
		id[x]=id[id[x]];
		x=id[x];
	}
	return x;
}

void Union(int p,int q)//合并p和q所在的树 
{
	int i=find(p);
	int j=find(q);
	if(i==j)
		return ;
	if(size[i]>size[j])
	{
		id[j]=i;
		size[i]+=size[j];
	}	
	else
	{
		id[i]=j;
		size[j]+=size[i];
	}
}
int Kruskal(int n,int m)//n个城市,m条边 
{
	int edge=0,price=0;//edge代表以连接的边数,price代表花费 
	sort(r,r + m,cmp);//对所有的道路按照价钱排序 
	for(int i=0;i<m && edge!=n-1;i++)
	{//连通n个城市,最少只需要n-1条路,所以edge!=n-1
		if( find(r[i].u) != find(r[i].v) )// 若两个城市没有联通 
		{
			Union(r[i].u ,r[i].v );//连通两条道路
			edge++;//边数加一
			price+=r[i].w ;//最小花费增加
		}
	}
	return price;//返回最小花费
}
int main()
{
	int n,m,i;
	int u,v,w;
	scanf("%d %d",&n,&m); //n个城市,m条道路 
	//cin >> n >> m;
	for(i=0;i<n;i++)
	{
		id[i]=i;//初始化id,父亲节点为自身 
		size[i]=1;//初始化size,i所在的树只有自己一个 
	} 
	for(i=0;i<m;i++)//输入道路数据,储存在结构体内
	{
		scanf("%d %d %d",&u,&v,&w);
		//cin >> u >> v >> w;
		r[i].u =u-1;//将城市编号减一以配合前面size,id
		r[i].v =v-1;
		r[i].w =w;
	}
	int price;
	price=Kruskal(n,m);//最小花费
	printf("%d",price);
	//cout << price;
	return 0;
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值