7-6 最小生成树-prim

目录

题目描述

输入格式:

输出格式:

输入样例:

输出样例:

解题思路:

举个例子:

详细代码:

堆优化的prim详细代码:


题目描述

题目给出一个无向连通图,要求求出其最小生成树的权值。

温馨提示:本题请使用prim最小生成树算法。

输入格式:

第一行包含两个整数 N(1<=N<=1x104),M(1<=M<=2x106) 表示该图共有 N 个结点和 M 条无向边。

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

输出格式:

输出一个整数表示最小生成树的各边的长度之和。

输入样例:

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

输出样例:

7

解题思路:

思想与dijkstra相同

即从1号点开始,将其纳入集合,查找与其距离最短的边,只需记录该点的终点是否走过,

然后将这条边连接的点也纳入集合中,查找与这个集合距离最短的另一条边,依次进行下去。

举个例子:

根据输入样例

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

可以构建一个如图所示的无向图

定义最小生成树总权值sum=0

首先将1号点放入集合中,集合{1}

查找与集合1最近的边为1到2的边权值为2,总权值sum=sum+2=2

之后2进入集合{1,2}

查找与集合最近的边为1到3的边权值为2,总权值sum=sum+2=4

之后3进入集合{1,2,3}

查找与集合最近的边为1到4的边权值为3,总权值sum=sum+3=7

构建最小生成树如下图

此时集合中有{1,2,3,4}全都被标记在集合中

详细代码:

#include <iostream>
#include <cstring>
#include <algorithm> 
#include <vector>
using namespace std;

const int N=1e5+7;
struct asd{
	int x,y;
};
vector<asd>ljb[N];
int dist[N];//当前点i到集合的最短距离
int st[N];//等于1在集合中,等于0不在集合中
int n, m;
int prim(){
	memset(dist,0x3f,sizeof dist);
	int res=0;//计算生成树最小权值
	for (int i=0;i<n;i++) {
		int t=-1;
		for (int j=1;j<=n;j++) {
			if (st[j]==0&&(t==-1||dist[t]>dist[j]))//找到当前离集合最短距离的点
				t=j;
		}
		if(i)
		{
			res+=dist[t];
		}
		st[t]=1;
		for (int j=0;j<ljb[t].size();j++) {
			asd ls=ljb[t][j];
			dist[ls.x]=min(dist[ls.x],ls.y);//更新点到集合的最短距离
		}
	}
	return res;
}
int main() {
	scanf("%d %d",&n,&m);
	while(m--){
		int a,b,c;
		scanf("%d %d %d",&a,&b,&c);;
		ljb[a].push_back({b,c});//邻接表储存边信息
		ljb[b].push_back({a,c});
	}
	int t=prim();
	printf("%d",t);
}

堆优化的prim详细代码:

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;

const int N = 1e4+10;

typedef pair<int,int> PII;//用PTT替换pair<int,int>文本

vector<PII>ljb[N]; 
bool vis[N];//记录是否走过
int dis[N];
int n,m;

void prim()
{
	memset(dis,0x3f,sizeof dis);//将其他点到集合的距离设为无穷大
	dis[1] = 0;//初始化1到集合的距离
	int res = 0;
	priority_queue<PII,vector<PII>,greater<PII>> q;//优先队列,将距离更小的放在前面
	q.push({0,1});
	
	while(!q.empty())
	{
		int t = q.top().second;//取出当前出队的点
		q.pop();
		if(vis[t]) continue;//如果走过即跳过
		vis[t] = true;//走了则标记
		res += dis[t];//记录最小生成树的权值
		for(auto ls:ljb[t])//遍历与t相邻的点的距离
		{
			int k=ls.first,w=ls.second;
			if(dis[k]>w)//如果之前更新的相邻点的与当前点的距离大于现在更新的距离则替换
			{
				dis[k]=w;
				q.push({w,k});//将距离和该点入队
			}
		}
	}
	printf("%d",res);
}

int main()
{
	scanf("%d%d",&n,&m);
	
	for(int i=1;i<=m;i++)
	{
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		
		ljb[u].push_back({v,w});//邻接表存储边信息
		ljb[v].push_back({u,w});
	}
	prim();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

星与星熙.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值