洛谷——P3366 【模板】最小生成树

【题目描述】

如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz

数据规模:

对于 20% 的数据, M≤20。

对于 40% 的数据, M≤2500。

对于 70% 的数据, M≤10^4。

对于 100% 的数据: 50001≤N≤5000, 1≤M≤2×10^5, 10^41≤Zi​≤。

【输入】

第一行包含两个整数 N,M,表示该图共有 N 个结点和 M 条无向边。

接下来 M 行每行包含三个整数 Xi​,Yi​,Zi​,表示有一条长度为 Zi​ 的无向边连接结点 Xi​,Yi​。

【输出】

如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz

样例输入

4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3

样例输出

7

解题思路

这个题可以用 Prim 和 Kruskal 解题,首先讲讲 Prim 算法。

它是以某一个顶点为起点,遍历周围可以到达的顶点,从中找到两顶点的最小权值 min,找到后将最小权值加入——初始化后的 sum,并且将当前的最小权值最小权值赋为 0(因为后面接着要找最小权值,如果去掉这个 min 的值,那么第二轮循环以及后面的循环找到的最小权值不准确)。

这个题我们将下标为 1 的顶点设为起点,定义数组 lowcost 存储当前从起点,到以该下标为顶点的最小路径,定义数组 adjvex 存储它当前连接的顶点(比如 adjvex [3]=4,代表顶点 3 连接着顶点 4)。

之前想过为什么要额外把顶点 1 连接的权值存入 lowcost 数组,难道不可以直接用循环从 i=1 到 i=n吗?事实是也可以,不过在之前要把 lowcost 数组的值初始化为一个较大值。

代码如下:

#include<stdio.h>
int flag,sum,n,m,a[5005][5005];
void MiniSpanTree_Prim()
{
	int i,j,k,min;
	int adjvex[5005];//存储顶点间边的权值下标
	int lowcost[5005];//存储当前权值(如果值为 0,说明已经经过该点)
	lowcost[1]=0;//表示以顶点 1为起点,将顶点 1加入生成树 
	adjvex[1]=1;//因为顶点 1是起点,它的上端没有连接,所以将值赋为 1 
	for(i=2;i<=n;i++)//初始化,将起点与各顶点的权值存入,如果不可到达,就赋为一个较大值 
	{
		lowcost[i]=a[1][i];
		adjvex[i]=1;//此时这些顶点的上端都是顶点 1 
	}
	for(i=2;i<=n;i++)
	{
		min=99999999;//将 min赋为一个较大值 
		k=1;
		for(j=1;j<=n;j++)
		{
			if(lowcost[j]<min&&lowcost[j]!=0)//首先保证该顶点没有重复 
			{
				min=lowcost[j];//找最小值 
				k=j;//记录下标 
			}
		}
		if(min<99999999)
		{
			sum=sum+min;
			lowcost[k]=0;//加上权值后,将它赋为 0,否则会出错 
		}
		//如果不可以通过,也就是 min==99999999, 说明还有顶点未加入生成树,此时结束函数,并用 flag标记,结尾输出 orz 
		else
		{
			flag=1;
			return ;
		}
		for(j=2;j<=n;j++)
		{
			if(lowcost[j]!=0&&a[k][j]<lowcost[j])
			{
				lowcost[j]=a[k][j];
				adjvex[j]=k;
			}
		}
	}
}
int main()
{
	int i,j,x,y,z;
	scanf("%d %d",&n,&m);
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=n;j++)
		a[i][j]=99999999;//初始化 
	}
	for(i=0;i<m;i++)
	{
		scanf("%d %d %d",&x,&y,&z);
		if(z<a[x][y])
		{
			a[x][y]=z;//不要忘了是双向图 
		    a[y][x]=z;
		}
	}
	MiniSpanTree_Prim();
	if(flag==1)
	printf("orz");
	else
	printf("%d",sum);
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明里灰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值