最小生成树(Kruskal算法和Prim算法)--洛谷P3366

题目

在这里插入图片描述

题解

1.Prim算法

Prim的思想是将任意节点作为根,再找出与之相邻的所有边(用一遍循环即可),再将新节点更新并以此节点作为根继续搜

#include<bits/stdc++.h> 
using namespace std;

bool b[5001];
int ans=0,dis[5001],w[5001][5001],x,y,z,n,m;

void Prim()
{
	//以1作为根节点
	for(int i=0;i<=n;i++) dis[i]=w[1][i];
	dis[1]=0;
	b[1]=true;//标记1已经访问 
	for(int i=1;i<n;i++)
	{
		int k=0;
		for(int j=1;j<=n;j++)
		{ 
			//如果j还没有访问过且两点之间可连接
			if(!b[j]&&dis[j]<dis[k]) 
			k=j;//记录j 
		} 
		b[k]=true;
		ans+=dis[k];
		//不断更新根节点 
		for(int j=1;j<=n;j++)
		{
			if(dis[j]>w[k][j])
			//dis储存从k到j的最短距离  
				dis[j]=w[k][j];
		} 
	}
	printf("%d\n",ans);
}
int main()
{
	scanf("%d %d",&n,&m);//输入点数和边数
	for(int i=0;i<=n;i++)
	{
		//从i到j的边,若i,j重合,距离为0,不重合,统一赋值为正无穷 
		for(int j=i+1;j<=n;j++)
			w[i][j]=w[j][i]=0x7fffffff;
		w[i][i]=0;
	}
	while(m--)
	{
		scanf("%d %d %d",&x,&y,&z);//起点,终点,权值 
		w[x][y]=w[y][x]=min(w[x][y],z);//初始赋值 
	}
	Prim();
	return 0;
}

2.Kruskal算法

Kruskal算法的思想是优先选取权值较小的边,并依次连接,若出现环则跳过此边(用并查集来判断是否存在环)继续搜,直到已经使用的边的数量比总点数少一即可。

#include<bits/stdc++.h>
using namespace std;

int n,m,sum=0,k=0;//n端点总数,m边数,tot记录最终答案,k已经连接了多少边 
int fat[200010];//记录集体老大 

struct A 
{
	int from;
	int to;
	int dis;
}edge[200010];
bool cmp(A &a,A &b)
{
	return a.dis<b.dis;
}
/*返回团体老大,明确归属*/ 
int father(int x)
{
	if(fat[x]!=x)
	return father(fat[x]);
	else return x;
}
/*把y加入x团体*/ 
void unionn(int x,int y)//x为边的起点,y为边的终点
{
	fat[father(y)]=father(x);//x为y在团体中老大 
}
int main()
{
	scanf("%d%d",&n,&m);//输入点数,边数 
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&edge[i].from,&edge[i].to,&edge[i].dis);//起,终,权值 
	}
	for(int i=1;i<=n;i++) 
		fat[i]=i;//自己最开始就是自己的老大 (初始化) 
	sort(edge+1,edge+1+m,cmp);//按权值排序(kruskal的体现)
	for(int i=1;i<=m;i++)//从小到大遍历 
	{
		if(k==n-1) break;//n个点需要n-1条边连接 
		if(father(edge[i].from)!=father(edge[i].to))//假如不在一个团体 
		{
			unionn(edge[i].from,edge[i].to);//加入 
			sum+=edge[i].dis;//记录边权 
			k++;//已连接边数+1 
		}
	}
	printf("%d\n",sum);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45619006/article/details/106246718