最小生成树-Kruskal

最小生成树-Kruskal

Kruskal

Kruskal算法是求加权连通图的最小生成树的算法。算法用到了贪心的思想,即将边按照权值由小到大排序,若这条边的两个端点不属于同一个集合则合并到一个集合,若属于同一个集合即形成了环,则跳过这条边,继续选取,直到选取了n-1条边即可。

算法流程

  1. 将n个顶点,m条边的图G中所有的边,按权值的大小从小到大排序。
  2. 从权值小的边依次选取,若选取的边使生成树不形成回路,则选。否则舍弃,继续执行,直到选取n-1条边为止。

模板

int n,m;
int pre[maxn];
struct Edge{
	int u,v;
	int w;
}edge[maxe];
void init(){
	for(int i=1;i<=n;i++)
		pre[i]=i;
}
int find(int x){
	if(pre[x]!=x)	pre[x]=find(pre[x]);
	return pre[x];
}
void unionn(int i,int j){
	pre[find(j)]=find(i);
}
bool cmp(Edge& e1,Edge& e2){
	return e1.w<e2.w;
}
int Kruskal(){
	int ans=0,cnt=0;
	init();
	sort(edge+1,edge+m+1,cmp);
	for(int i=1;i<=m;i++){
		if(find(edge[i].u)!=find(edge[i].v)){
			unionn(edge[i].u,edge[i].v);
			ans+=edge[i].w;
			cnt++;
		}
		if(cnt==n-1)	break;
	}
	return ans;
}

例题

POJ-1251 Jungle Roads

POJ-1251

题目描述

题目背景

在茫茫的宇宙中有一个科技非常发达的文明–Z文明
就在今天,Z文明的人们通过先进的科技,完成了在自己所在的星系系统中的每一颗行星上都建立可生存环境的宏伟目标。
当然单单只建立可生存环境是不够的,对于生命个体来说,星球和星球之间的距离过于遥远,飞船直接飞行在两个星球间进行交通时间过于漫长。
为了保证星际旅行的速度,Z文明已经发明了虫洞产生器,可以扭曲两颗星球之间的空间。使得飞船可以在很短的时间内往返虫洞所连接的两个星球。
由于每个行星被开发的时间有早有晚,虫洞产生器在不同的星球之间布置的时间也有早有晚,有的年久失修,有的崭新出厂。导致每对虫洞产生器的维护代价不同

题目描述

Z文明刚刚完成星系中每颗行星的开发,他们现在想要留下一定数量的虫洞产生器,废弃掉其他的。在保证所有行星都能够连通的同时使得维护费用最少。现在给出
N个行星之间(1 < n < 27)的虫洞连接情况和每对虫洞产生器的维护费用,Z文明的人们想要知道这个最少的维护费用是多少。
Z文明的计算机编程水平实在是太烂了,现在他们来求助聪明的太阳系人来帮他们用编程解决这个问题。Z文明承诺,如果解决这个问题,将告诉太阳系文明他们的
虫洞核心技术,这样ajil就能更好地研究虫洞了。你能帮助ajil吗(不是)你能帮助Z文明吗。

Input

(最多有100次询问)对于每次询问: 第一行给出行星数量n; 接下来第二行到第n+1行, 给出当前行星名称a ,当前行星上的虫洞产生器数量k ,之后k组数据表示 通过虫洞发生器与a行星相连的行星的名称以及该对虫洞产生器的维护费用, (多组数据输入,当行星数量为0时,输入结束) 1 < n < 27, 0 <= k <= 15

Output

每组数据输出一行最小维护费用

Sample Input

9
A 2 B 12 I 25
B 3 C 10 H 40 I 8
C 2 D 18 G 55
D 1 E 44
E 2 F 60 G 38
F 0
G 1 H 35
H 1 I 35
3
A 2 B 10 C 40
B 1 C 20
0

Sample Output

216
30

思路分析

最小生成树模板题目

详细代码
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;

int n,edge_num,ans;
int fa[30];
struct Edge{
	int u,v;
	int w;
}edge[600];
void init(){
	for(int i=1;i<=n;i++)
		fa[i]=i;
}
int find(int x){
	if(fa[x]!=x)	fa[x]=find(fa[x]);
	return fa[x];
}
void unionn(int i,int j){
	fa[find(j)]=find(i);
}
bool cmp(Edge& e1,Edge& e2){
	return e1.w<e2.w;
}

int main(){
	while(cin>>n && n){
		ans=0;edge_num=0;
		int cnt=0;
		for(int i=1;i<n;i++){
			char ch;
			int k;
			cin>>ch>>k;
			while(k--){
				char x;int y;
				cin>>x>>y;
				edge[++edge_num].u=ch-'A'+1;
				edge[edge_num].v=x-'A'+1;
				edge[edge_num].w=y;
			}
		}
		init();
		sort(edge+1,edge+edge_num+1,cmp);
		for(int i=1;i<=edge_num;i++){
			if(find(edge[i].u)!=find(edge[i].v)){
				unionn(edge[i].u,edge[i].v);
				ans+=edge[i].w;
				cnt++;
			}
			if(cnt==n-1)	break;
		}
		cout<<ans<<endl;
	}
	return  0; 
} 

POJ-1287 Networking

POJ-1287

题目描述

存在许多点和点与点之间的路径,路径长度不一,点到点之间可能存在多条路径。挑选部分路径使得所有点连通且总路径长度最小。

Input

多样例输入。每个样例都保证有解。
每个样例的第一行有两个整数,P(点的个数),R(点与点之间的路径总数)。
接下来的R行输入路径,每行含3个整数,前两个数表示连接的两个点,第三个数表示路径长度。
当P为0时输入结束。样例与样例之间存在空行
P最大为50,路径长度最大为100,路径数没有限制。
i和j之间的路径可以表示为 i j 或 j i

Output

对于每个样例,输出一个数表示设计出的路径总长度

Sample Input

1 0

2 3
1 2 37
2 1 17
1 2 68

3 7
1 2 19
2 3 11
3 1 7
1 3 5
2 3 89
3 1 91
1 2 32

5 7
1 2 5
2 3 7
2 4 8
4 5 11
3 5 10
1 5 6
4 2 12

0

Sample Output

0
17
16
26
思路分析

最小生成树模板题目

详细代码
#include<iostream>
#include<algorithm>
using namespace std;
int p,r,edge_num,fa[55];
struct Edge{
	int u,v,w;
}edge[3000];
bool cmp(Edge e1,Edge e2){
	return e1.w<e2.w;
}
void init(){
	for(int i=1;i<=p;i++)	
		fa[i]=i;
}
int find(int x){
	if(fa[x]!=x)	
		fa[x]=find(fa[x]);
	return fa[x];
}
void unionn(int i,int j){
	fa[find(j)]=find(i);
}
int main(){
	while(cin>>p && p){
		cin>>r;
		int ans=0,k=0;
		edge_num=0;
		while(r--){
			int x,y,t;
			cin>>x>>y>>t;
			edge[++edge_num].u=x;
			edge[edge_num].v=y;
			edge[edge_num].w=t;
		}
		sort(edge+1,edge+edge_num+1,cmp);
		init();
		for(int i=1;i<=edge_num;i++){
			if(find(edge[i].u)!=find(edge[i].v)){
				unionn(edge[i].u,edge[i].v);
				ans+=edge[i].w;
				k++;
			}
			if(k==p-1)	break;
		}
		cout<<ans<<endl;
	}
	return 0; 
}

POJ-2377 Bad Cowtractors

POJ-2377

题目描述

贝茜受雇在农场主约翰的N个谷仓(2 <= N <= 1000)之间建立一个廉价的互联网网络。FJ已经做了一些调查,发现了M (1 <= M <= 20,000)对谷仓之间可能的连接路径。每个可能的连接路由都有一个相关的成本C (1 <= C <= 100,000)。农民约翰想要在连接网络上花费最少的钱;他甚至都不想付钱给贝茜。意识到农场主约翰不会给她钱,贝茜决定做最坏的工作。她必须决定一组连接安装,所以(我)这些连接的总成本是尽可能大,(2)所有的谷仓都连接在一起(这样就可以达到任何谷仓从其他仓库通过安装连接的路径),和(iii),这样没有周期之间的连接(农民约翰很容易能够检测)。条件(ii)和(iii)确保最后的连接集看起来像一个“树”。

Input

第1行:两个空格分隔的整数:N和M线2 . .M+1:每行包含三个以空格分隔的整数A、B和C,它们描述了成本为C的仓库A和仓库B之间的连接路由。

Output

*第1行:单个整数,包含连接所有仓库的最昂贵树的价格。如果不可能连接所有的谷仓,输出-1。

Sample Input

5 8
1 2 3
1 3 7
2 3 10
2 4 4
2 5 8
3 4 6
3 5 2
4 5 17

Sample Output

42

Hint

OUTPUT DETAILS:

The most expensive tree has cost 17 + 8 + 10 + 7 = 42. It uses the following connections: 4 to 5, 2 to 5, 2 to 3, and 1 to 3.

思路分析

最大生成树,使用Kruskal算法,优先选取权值最大的边即可。

详细代码
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;

const int maxe=20002;
const int maxn=1002;
int n,m;
int pre[maxn];
struct E{
	int u,v,w;
	bool operator <(const E& a) const {
		return a.w<w;
	}
}edge[maxe];
void init(){
	for(int i=1;i<=n;i++)	pre[i]=i;
}
int find(int x){
	if(x==pre[x])	return x;
	return pre[x]=find(pre[x]); 
}
void unionn(int x,int y){
	int rx=find(x);
	int ry=find(y);
	if(rx!=ry){
		pre[rx]=ry;
	} 
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=0;i<m;i++){
		scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
	}
	sort(edge,edge+m);
	int cnt=0,ans=0;
	init();
	for(int i=0;i<m;i++){
		int x=edge[i].u,y=edge[i].v,w=edge[i].w;
		if(find(x)!=find(y)){
			ans+=w;
			unionn(x,y);
			cnt++;
		}
	}
	if(cnt==n-1)	printf("%d\n",ans);
	else	printf("-1\n");
	return 0;
}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=0;i<m;i++){
		scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
	}
	sort(edge,edge+m);
	int cnt=0,ans=0;
	init();
	for(int i=0;i<m;i++){
		int x=edge[i].u,y=edge[i].v,w=edge[i].w;
		if(find(x)!=find(y)){
			ans+=w;
			unionn(x,y);
			cnt++;
		}
	}
	if(cnt==n-1)	printf("%d\n",ans);
	else	printf("-1\n");
	return 0;
}
  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值