Kruskal算法与Prim算法求最小生成树

kruskal/Prim算法求最小生成树

 传送门:P3366[模板]最小生成树

最小生成树的定义

 一个有 n n n个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n n n个结点,并且有保持图连通的最少的边(即 n − 1 n-1 n1条边),并且这 n − 1 n-1 n1条边的权值之和是所有生成树中最小的极小连通子图,那么这个连通子图就叫做最小生成树。

kruskal算法的原理说明

 运用贪心思想,按边权的大小取边依次用并查集合并,如果加入边数 = n − 1 =n-1 =n1,那么就说明找到了最小生成树。若设整张图的边的个数为 m m m,则 k r u s k a l kruskal kruskal算法的时间复杂度为 O ( m log ⁡ m ) O(m\log m) O(mlogm)
 参考代码

#include<bits/stdc++.h>
#define Maxn 200010
using namespace std;

struct node{
	int u,v,c;
}edge[Maxn];

int n,m,father[5001];

inline int read(){
	int x=0;char c=getchar();
	while(c<'0'||c>'9')c=getchar();
	while(c>='0' and c<='9'){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
	return x;
}

inline bool cmp(node a,node b){
	return a.c<b.c;
}

inline int getfather(int x){
	if(father[x]==x)return x;
	father[x]=getfather(father[x]);
	return father[x];
}

inline void combine(int i,int j){
	i=getfather(i);
	j=getfather(j);
	if(i!=j)father[i]=j;
}

int main(){
	n=read(),m=read();
	for(int i=1;i<=m;i++)
		edge[i].u=read(),edge[i].v=read(),edge[i].c=read();
	
	for(int i=1;i<=n;i++)
		father[i]=i;
		
	sort(edge+1,edge+m+1,cmp);
	
	int tmp=0,ans=0;
	bool check=false;
	
	for(int i=1;i<=m;i++){
		int a=edge[i].u,b=edge[i].v;
		if(getfather(a)!=getfather(b)){
			combine(a,b);
			tmp++;
			ans+=edge[i].c;
		}
		else continue;
		if(tmp==n-1){
			check=true;
			break;
		}
	}
	
	if(check)
		cout<<ans<<endl;
	else
		cout<<"orz"<<endl;

	return 0;
}

Prim算法

P r i m Prim Prim算法适合稠密图,即边数较多而点较少的情况,时间复杂度为 O ( n 2 ) O(n^2) O(n2),堆优化的情况下,如果点数为 m m m,边数为 n n n,可以达到 O ( n log ⁡ m ) O(n\log m) O(nlogm)
 这个算法的思想很简单,就是每次寻找一条由已加入集合的点和与它们相邻的没加入集合的点的权值最小边,进行 n − 1 n-1 n1次就找出来了,也是贪心思想,实现就是随便找一个初始节点,然后建一个最小堆(边小的先 p o p pop pop出来),把该节点的 v i s vis vis值置为 1 1 1,遍历该节点相邻的节点,如果没有被 v i s vis vis标记过,就加入边到堆中,扫完了以后处理堆中数据,如果弹出的边被标记过就 p o p pop pop,没有就取出来,把边通往的节点置为 k e y key key,下次就加入 k e y key key节点有关没有标记过的边。一直循环,由于最小生成树边数与节点的关系为 m = n − 1 m=n-1 m=n1,所以要循环 n − 1 n-1 n1次,把每一次堆弹出边的值累加起来就是最小生成树的值了

 参考代码

#include<bits/stdc++.h>
#define in read()
#define re register 
#define pii pair<int,int>
#define mp make_pair
#define sd second
#define fl first
#define MAXN 200050
#define MAXM MAXN<<1
#define N 5005
using namespace std;

int dis[N],n,m,cnt=0,ans=0;
bool vis[N];
int nex[MAXM],first[MAXN],to[MAXM],val[MAXM],tot=0;

priority_queue<pii,vector<pii>,greater<pii> >q;

inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
	return x*f;
}

inline void addedge(int u,int v,int w){
	nex[++tot]=first[u];
	first[u]=tot;
	to[tot]=v;val[tot]=w;
}

int main(){
	n=in,m=in;
	for(re int i=1;i<=m;i++){
		int u=in,v=in,w=in;
		addedge(u,v,w);
		addedge(v,u,w);
	}
	
	memset(dis,0x3f3f3f3f,sizeof dis);
	q.push(mp(dis[1],1));dis[1]=0;//入堆第一个点
	
	while(!q.empty() and cnt<n){
		int u=q.top().sd;q.pop();
		if(vis[u])continue;
		vis[u]=true;cnt++;ans+=dis[u];
		
		for(int e=first[u];e;e=nex[e]){
			int v=to[e];
			if(!vis[v] and dis[v]>val[e]){
				dis[v]=val[e];//先更改v
				q.push(mp(dis[v],v));//然后入堆
			}			
		}
	}
	
	for(int i=1;i<=n;i++)
		if(!vis[i]){
			cout<<"orz"<<'\n';
			return 0;
		}//判断是否连通
	cout<<ans<<'\n';
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值