最小生成树

文章介绍了计算无向连通图最小生成树的两种经典算法——Prim算法和Kruskal算法。Prim算法基于贪心策略,类似于Dijkstra最短路径,从任意点开始逐步扩展,每次选择与当前集合距离最小的边。Kruskal算法利用并查集,按边的权重排序,依次尝试连接不同集合的节点,避免形成环路。
摘要由CSDN通过智能技术生成

一,定义:

将无向连通图连起来的生成树中需要的权值最小的生成树为最小生成树

主要有两种算法

prim算法(暴力O(n^2+m)还有kruskal算法(mlogm)。

P3366 【模板】最小生成树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

以下代码都是写这模板题的

prim算法

思路:贪心(最短路)

prim写法几乎与dijkstra没什么区别。思想就是首先以任意点为起点,然后遍历与他有连接的点,每次选权值最小的边对应的点(同时也更新其他点到起点这个集合(不是到起点)的最短距离即可)

#include <bits/stdc++.h>
using namespace std;
#define ll               long long
#define endl             "\n"
#define int              long long
typedef pair<int, int> pii;
const int INF = 0x3f3f3f3f;         //int型的INF
const int N = 2e5 + 10;
struct node
{
	int next,to;
	int w;
} edge[N<<1];
int head[N],num;
int dis[N];
bool vis[N];

void add(int u,int v,int w)
{
	edge[++num].next=head[u];
	edge[num].to=v;
	edge[num].w=w;
	head[u]=num;
}

int prim(int n)
{
	int ans=0;
	int cnt=0;
	fill(dis,dis+n+1,INF);//初始赋值所有点到起点集合距离无穷大
	dis[1]=0;
	priority_queue<pii,vector<pii>,greater<pii>>q;//堆优化
	q.push({dis[1],1});
	while(!q.empty())
		{
			int u=q.top().second;
			q.pop();
			if(vis[u])continue;//对于u,取他离集合最短时的边
			vis[u]=1;
			cnt++;//记录存入集合的点+1
			ans+=dis[u];//一旦取了,加入这个边
			for(int i=head[u]; i; i=edge[i].next)
				{
					int v=edge[i].to,w=edge[i].w;//更新u连接的其他点v,更新v点们到集合的最短距离
					if(dis[v]>w)//区别dij,这里更新其到集合(不是到起点)最短边即可
						{
							dis[v]=w;
							q.push({dis[v],v});
						}
				}
		}
	return (cnt==n?ans:-1);//如果存入了图的所有点,说明连通
}

void mysolve()
{
	int n,m;
	cin>>n>>m;
	int x,y,w;
	while(m--)cin>>x>>y>>w,add(x,y,w),add(y,x,w);
	int ans=prim(n);
	if(ans==-1)cout<<"orz"<<endl;
	else cout<<ans<<endl;
}

int32_t main()
{
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	ll t=1;
	//cin >> t;
	while (t--)
		{
			mysolve();
		}
	system("pause");
	return 0;
}

kruskal算法

思路:并查集

克鲁斯卡尔思路是一开始把n个点看成n个独立的集合,每次寻找图中的最短边(没有使用过或者舍弃过),然后如果这条边可以使图中的集合-1,那么取,否则舍去。

所以预处理就是把图中的边排序

如果最后图中只剩一个集合,成功获得最小生成树

#include <bits/stdc++.h>
using namespace std;
#define ll               long long
#define endl             "\n"
#define int              long long
const int N = 2e5 + 10;

struct node
{
	int u,v,w;
	bool operator<(const node&k)
	{
		return w<k.w;
	}
} edge[N<<1];
int fa[N];
int find(int x)
{
	if(fa[x]!=x)fa[x]=find(fa[x]);
	return fa[x];
}

int kruskal(int n,int m)
{
	int cnt=n,ans=0;//cnt记录图中集合个数
	sort(edge+1,edge+1+m);//排序边
	for(int i=1; i<=m; ++i)
		{
			int u=edge[i].u,v=edge[i].v,w=edge[i].w;
			int fu=find(u),fv=find(v);
			if(fu==fv)continue;//如果属于一个集合,该边舍去
			ans+=w;
			fa[fv]=fu;//合并集合
			cnt--;
		}
	return (cnt==1?ans:-1);//集合为一说明图连通
}

void mysolve()
{
	int n,m;
	cin>>n>>m;
	for(int i=1; i<=n; ++i)fa[i]=i;
	for(int i=1; i<=m; ++i)cin>>edge[i].u>>edge[i].v>>edge[i].w;
	int ans=kruskal(n,m);
	if(ans==-1)cout<<"orz"<<endl;
	else cout<<ans<<endl;
}

int32_t main()
{
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	ll t=1;
	//cin >> t;
	while (t--)
		{
			mysolve();
		}
	system("pause");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值