最小生成树(普利姆)(克鲁斯卡尔)

【模板】最小生成树(普利姆)(克鲁斯卡尔)

题目描述

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

输入格式

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

接下来 \(M\) 行每行包含三个整数 \(X_i,Y_i,Z_i\),表示有一条长度为 \(Z_i\) 的无向边连接结点 \(X_i,Y_i\)

输出格式

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

样例 #1

样例输入 #1

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

样例输出 #1

7

提示

数据规模:
对于 \(100\%\) 的数据:\(1\le N\le 5000\)\(1\le M\le 2\times 10^5\)\(1\le Z_i \le 10^4\)

样例解释:

所以最小生成树的总边权为 \(2+2+3=7\)

思路

这是一道最小生成树的模板题,可以直接用克鲁斯卡尔或普利姆算法做。

克鲁斯卡尔算法

这是一个求最小生成树的算法,它是一个基于贪心的算法,具体思路:
现在我们先把所有边按边权排序,从小到大遍历每个边,如果这条边连着的两个点不在同一个集合内,就合并两个点,直到所有点都在一个集合内,就完成了算法。

代码

#include<bits/stdc++.h>
using namespace std;
struct edge{
	int u,v,w;
};
vector<edge> v;
bool cmp(edge a,edge b){
	return a.w<b.w;
} 
int fa[1000001];
int find(int i){
	return fa[i]==i?i:fa[i]=find(fa[i]);
}
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=m;i++){
		int u,to,w;
		scanf("%d%d%d",&u,&to,&w);
		v.push_back(edge{u,to,w});
	}
	sort(v.begin(),v.end(),cmp);
	int cnt=0,ans=0;
	for(int i=0;i<m;i++){
		if(cnt==n-1)break;
		int fx=find(v[i].u),fy=find(v[i].v);
		if(fx!=fy){
			fa[fx]=fy;
			ans+=v[i].w;
			cnt++;
		}
	}
	if(cnt!=n-1)puts("orz");
	else printf("%d",ans);
	return 0;
}

普利姆算法

这也是一个求最小生成树的算法,它也是一个基于贪心的算法,具体思路:
我们有两个集合:\(A\) \(B\),分别表示没被加入最小生成树的点和被加入最小生成树的点,首先把点\(1\)加入集合\(B\)内,然后遍历\(B\)的所有出边,找到边权最小的一个,把这条边连着的点加入\(B\)内,直到\(A\)空,就完成了算法。

代码

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
struct edge{
	int v,w;
	edge(int b,int c){
		v=b;
		w=c;
	}
};
vector<edge> a[50001];
priority_queue<edge> p;//B的所有出边
bool operator <(edge a,edge b){
	return a.w>b.w;
}
bool vis[50001];
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		a[u].push_back(edge{v,w});
		a[v].push_back(edge{u,w});
	}
	int cnt=1,ans=0,use=1;
	vis[1]=1;
	for(int i=0;i<a[1].size();i++){
		p.push(a[1][i]);
	}
	cnt=1;
	while(cnt!=n){
		edge ne=p.top();
		p.pop();
		while(vis[ne.v]&&!p.empty()){
			ne=p.top();
			p.pop();	
		}
		ans+=ne.w;
		for(int j=0;j<a[ne.v].size();j++){
			p.push(a[ne.v][j]);
		}
		cnt++;
		vis[ne.v]=1;
	}
	for(int i=1;i<=n;i++){
		if(!vis[i]){printf("orz");return 0;}
	}
	printf("%d",ans);
	return 0;
}
posted @ 2022-07-24 20:32  maniubi  阅读(56)  评论(0编辑  收藏  举报