最小生成树(51Nod+Prim+Kruskal)

2 篇文章 0 订阅
1 篇文章 0 订阅

N个点M条边的无向连通图,每条边有一个权值,求该图的最小生成树。

输入

第1行:2个数N,M中间用空格分隔,N为点的数量,M为边的数量。(2 <= N <= 1000, 1 <= M <= 50000)
第2 - M + 1行:每行3个数S E W,分别表示M条边的2个顶点及权值。(1 <= S, E <= N,1 <= W <= 10000)

输出

输出最小生成树的所有边的权值之和。

输入示例

9 14
1 2 4
2 3 8
3 4 7
4 5 9
5 6 10
6 7 2
7 8 1
8 9 7
2 8 11
3 9 2
7 9 6
3 6 4
4 6 14
1 8 8


输出示例

37 

 解题思路:解决这道题可以使用pime肯Kruskal,期间会使用并查集的内容,网上有许多关于prime和Kruskal以及并查集的博客

在这里只介绍prim算法,Kruskal和并查集给出参考博客

参考博客:

并查集参考博客:数据结构 -- 并查集_Enstein_Jun的博客-CSDN博客_并查集类只能int么

Kruskal算法参考博客:最小生成树之Kruskal算法_Enstein_Jun的博客-CSDN博客_kruskal算法时间复杂度

prime算法详解:

普里姆算法—Prim算法

算法思路: 
首先就是从图中的一个起点a开始,把a加入U集合,然后,寻找从与a有关联的边中,权重最小的那条边并且该边的终点b在顶点集合:(V-U)中,我们也把b加入到集合U中,并且输出边(a,b)的信息,这样我们的集合U就有:{a,b},然后,我们寻找与a关联和b关联的边中,权重最小的那条边并且该边的终点在集合:(V-U)中,我们把c加入到集合U中,并且输出对应的那条边的信息,这样我们的集合U就有:{a,b,c}这三个元素了,一次类推,直到所有顶点都加入到了集合U。

下面我们对下面这幅图求其最小生成树:

这里写图片描述

假设我们从顶点v1开始,所以我们可以发现(v1,v3)边的权重最小,所以第一个输出的边就是:v1—v3=1: 
这里写图片描述

然后,我们要从v1和v3作为起点的边中寻找权重最小的边,首先了(v1,v3)已经访问过了,所以我们从其他边中寻找,发现(v3,v6)这条边最小,所以输出边就是:v3—-v6=4 
这里写图片描述

然后,我们要从v1、v3、v6这三个点相关联的边中寻找一条权重最小的边,我们可以发现边(v6,v4)权重最小,所以输出边就是:v6—-v4=2. 
这里写图片描述

然后,我们就从v1、v3、v6、v4这四个顶点相关联的边中寻找权重最小的边,发现边(v3,v2)的权重最小,所以输出边:v3—–v2=5 
这里写图片描述

然后,我们就从v1、v3、v6、v4,v2这2五个顶点相关联的边中寻找权重最小的边,发现边(v2,v5)的权重最小,所以输出边:v2—–v5=3 
这里写图片描述

最后,我们发现六个点都已经加入到集合U了,我们的最小生成树建立完成。

AC代码:

    一:kruskal代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
const int maxn=5e4+10;
struct node{
	int a,b,c;
}tre[maxn];
int rank[maxn],vis[maxn];
bool cmp(node a,node b)
{
	return a.c<b.c;//按权值从小到大排序 
}
void init(int n) 
{
	for(int i=0;i<=n;i++)
	{
		vis[i]=i;
		rank[i]=0;//数的高度 
	}
}
int findx(int x)
{
	int root=(x==vis[x]?x:findx(vis[x]));
	while(x!=root)//优化,将同一树的节点都指向根节点 
	{
		int t=vis[x];
		vis[x]=root;
		x=t;
	}
	return root;
}
void merge(int x,int y) 
{
	int fx=findx(x);//寻找根节点 
	int fy=findx(y);
	if(rank[fx]<rank[fy])
	{
		vis[fx]=fy;
	}
	else
	{
		vis[fy]=fx;
		if(rank[fx]=rank[fy]) rank[fx]++;
	}
}
//n为边的数量,m为顶点的数量 
int kruskal(int n,int m)
{
	int nedge=0,res=0;
	for(int i=1;i<=n&&nedge!=m-1;i++)
	{
		if(findx(tre[i].a)!=findx(tre[i].b))
		{
			merge(tre[i].a,tre[i].b);
			res+=tre[i].c;
			nedge++;
		}	
	}
//	if(nedge<m-1) 
//		return -1;
//	else 
		return res;
}
int main()
{
	int n,m;
	cin>>n>>m;
	init(n);
	for(int i=1;i<=m;i++)
	{
		cin>>tre[i].a>>tre[i].b>>tre[i].c;
	}
	sort(tre+1,tre+m+1,cmp);
	cout<<kruskal(m,n)<<endl;
	return 0;
}

prim解法代码:

#include<iostream>
#include<cstring>
#define mem(a) memset(a,0,sizeof(a))
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1e3+10;
int cost[maxn][maxn];//记录两点之间的距离,不存在标记为无穷大 
int mincost[maxn];//从x集合出发到每个顶点的最小值
bool used[maxn];//判断当前节点是否在集合中
int n,m;

int prim()
{
	memset(mincost,INF,sizeof(mincost));
	mem(used);
	mincost[1]=0;//赋初值 
	
	int ans=0;
	while(true)
	{
		int v=-1;
		for(int u=1;u<=n;u++)
			if(!used[u]&&(v==-1||mincost[u]<mincost[v]))
				v=u;
		if(v==-1)
			break;//条件成立说明所有的点都已经加入
		used[v]=true;//标记点v已经取过	
		ans+=mincost[v]; //将边的长度加进去;
		//然后不断的更新mincost数组,从x集合出发到每个顶点的最小值,x集合表示还没有取过的点,
		//显然mincost[v]没有取过的点到每一个取过的点的最小距离(仔细阅读prim算法思想) 
		for(int u=1;u<=n;u++)
		{
			if(!used[u])
			mincost[u]=min(mincost[u],cost[v][u]);
		} 
	}
	return ans; 
}
int main()
{
	cin>>n>>m;
	memset(cost,INF,sizeof(cost));
	int x,y,z;
	for(int i=0;i<m;i++)
	{
		cin>>x>>y>>z;
		cost[x][y]=z;
		cost[y][x]=z;
	}
	int k=prim();
	cout<<k<<endl;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值