最小生成树

题目大意

例题链接
普通的最小生成树,给出 n n n个农场,求最短需要多长的光纤才能连接所有的农场。

解题思路

既然是最普通的最小生成树,那么就需要了解两种算法。

prim算法

prim算法采用的是蓝白点思想,把已进入最小生成树的点标为白点,未进入的标为蓝点,具体如下的例子。
如图,初始时所有点都未加入最小生成树, m i n 1 = 0 , m i n [ 2 , 3 , 4 , 5 ] = ∞ 。 m i n min_1=0,min[2,3,4,5]=∞。min min1=0,min[2,3,4,5]=min表示每个店离白点的最短距离,设初始点1为白点。
在这里插入图片描述
第一次循环找到离白点距离最小的蓝点1,将它加入最小生成树, a n s + = m i n 1 ans+=min_1 ans+=min1在这里插入图片描述
接下来更新与点1相连的点与白点的最小距离, m i n [ 2 , 3 , 4 , 5 ] = [ 2 , 4 , 7 , ∞ ] min[2,3,4,5]=[2,4,7,∞] min[2,3,4,5]=[2,4,7,]
接着再找到离白点最近的蓝点,也就是点2,将它加入最小生成树。 a n s + = m i n 2 ans+=min_2 ans+=min2
在这里插入图片描述
再次更新所有点与白点的最小距离, m i n [ 3 , 4 , 5 ] = [ 1 , 7 , 2 ] min[3,4,5]=[1,7,2] min[3,4,5]=[1,7,2]
再找到与当前点2相邻的最近的蓝点,也就是点3,将它加入最小生成树 a n s + = m i n 3 ans+=min_3 ans+=min3

在这里插入图片描述
以此类推,使最后所有点都为白点,得到MST=6。一共进行 n n n次循环,每次循环都将一个点加入最小生成树,一共添加 n − 1 n-1 n1条边,最后 n n n个点全部加入最小生成树,时间复杂度为 O ( n 2 ) O(n^2) O(n2)

kruskal算法

kruskal采用的是并查集,每次枚举距离最小的两个点,如果这两个点不存在于一个集合,就表示这两个点未与最小生成树连接,那么就把它们连接起来,当连接了 n − 1 n-1 n1条边时,则所有的点都已被连接。
需要初始化将每两个点的距离先排序一遍。
给出一个这样的图。
在这里插入图片描述
首先每个店都不是连通的。

在这里插入图片描述
先找到距离最短的两个点,也就是点1和点3,距离是1。将它们连接起来。
在这里插入图片描述
其次再找到点4和点6,距离为2,连接
在这里插入图片描述
以此类推,直到将所有的点都连接,也就是连接了 n − 1 n-1 n1条边后,我们就求出了最小生成树。
在这里插入图片描述
因为最后遍历时只需要 O ( E ) O(E) O(E)的时间复杂度( E E E为边数),因此kruskal的时间复杂度为排序所花费的 O ( E ∗ l o g 2 E ) O(E*log_2E) O(Elog2E)

代码

prim算法

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,g[105][105],minn[105];
bool v[105];
int main()
{
	cin>>n;
	memset(minn,0x7f,sizeof(minn));
	minn[1]=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			cin>>g[i][j];
	for(int i=1;i<=n;i++)
	{
		int p=0;
		for(int j=1;j<=n;j++)
			if(!v[j]&&minn[j]<minn[p])
				p=j;
		v[p]=1;
		for(int j=1;j<=n;j++)
			if(!v[j]&&g[p][j]<minn[j])
				minn[j]=g[p][j];
	}
	int ans=0;
	for(int i=1;i<=n;i++)
		ans+=minn[i];
	cout<<ans;
	return 0;
} 

kruskal算法

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,tot,minn[105],p[105],head[105];
bool v[105];
struct ap{
	int x,y,w;
}g[10005];
int find(int x)
{
	if(p[x]==x) return x;
	return p[x]=find(p[x]);
}
void join(int x,int y)
{	
	p[find(x)]=p[find(y)];
}
void app(int x,int y,int w)
{
	g[++tot].x=x;
	g[tot].y=y;
	g[tot].w=w;
}
bool cmp(ap x,ap y)
{
	return x.w<y.w;
}
int main()
{
	cin>>n;
	memset(minn,0x7f,sizeof(minn));
	minn[1]=0;
	for(int i=1;i<=n;i++) p[i]=i;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			int x;
			cin>>x;
			if(x) app(i,j,x);
		}
	int ans=0,k=0;
 	sort(g+1,g+tot+1,cmp);
	for(int i=1;i<=tot;i++)
	{
		if(find(g[i].x)!=find(g[i].y))
		{
			join(g[i].x,g[i].y);
			ans+=g[i].w;
			k++;
		}
		if(k==n-1) break;
	}
	cout<<ans; 
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值