【图论】最小生成树

 

目录

prim

思想

朴素做法 n^2+m

set优化 (n+m) logn

kruskal

思想

朴素做法 mlogm 


例题:代码源 最小生成树1

题目链接:代码源 最小生成树1

prim
思想

        首次初始化每个点的距离为0x3f3f3f3f,先任意找一个点,然后遍历剩下的所有的点找到距离这个点最近的点,更新距离,把这个点标记,继续用这个点更新与他相连并且未标记点的距离, 距离为两个相连点的边,然后继续遍历未标记的点加入集合,遍历n-1次。       

        与dijkstra的区别:dist[]数组表示被标记点和当前点的最短距离,而不是与原点

朴素做法 n^2+m
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int N = 1001;

struct node{
	int y,v;
	node(int _y,int _v){
		y=_y,v=_v;
	};
};

vector<node> edge[N];
int n,m,dist[N];
bool b[N];

void prim(){
	memset(b,false,sizeof b);
	memset(dist,127,sizeof dist);
	dist[1]=0;
	int ans=0;
	for(int i=1;i<=n;i++){
		int x=-1;
		for(int j=1;j<=n;j++){
			if(!b[j]&&dist[j]<1<30){
				if(x==-1||dist[j]<dist[x]){
					x=j;
				}
			}
		}
		
		b[x]=true;
		ans+=dist[x];
		for(auto j:edge[x]){
			dist[j.y]=min(dist[j.y],j.v);
		}
	}
	cout<<ans<<"\n";
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int x,y,z;
		cin>>x>>y>>z;
		edge[x].push_back(node(y,z));
		edge[y].push_back(node(x,z));
	}
	prim();

	
	return 0;	

}

set优化 (n+m) logn
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;

const int N = 50005;

struct node{
	int y,v;
	node(int _y,int _v){
		y=_y,v=_v;
	};
};

set<pii> q;
vector<node> edge[N];
int n,m,dist[N];
bool b[N];

void prim(){
	memset(b,false,sizeof b);
	memset(dist,127,sizeof dist);
	
	dist[1]=0;
	q.clear();
	
	for(int i=1;i<=n;i++){
		q.insert({dist[i],i});
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		int x=q.begin()->second;
		q.erase(q.begin());
		ans+=dist[x];
		b[x]=true;
		for(auto j:edge[x]){
			if(!b[j.y]&&j.v<dist[j.y]){
				q.erase({dist[j.y],j.y});
				dist[j.y]=j.v;
				q.insert({dist[j.y],j.y});
			}
		}
	}
	cout<<ans<<"\n";
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int x,y,z;
		cin>>x>>y>>z;
		edge[x].push_back(node(y,z));
		edge[y].push_back(node(x,z));
	}
	prim();

	
	return 0;	

}

kruskal
思想

        按边权从小到大排序,每次枚举一条边连接的两个点,如果两个顶点不在同一个集合,就合并集合,如果在同一个集合,就不做,直到最后集合为1,就结束。(如果本身知道是联通块,就遍历结束就一定存在最小生成树,不需要判断)

朴素做法 mlogm 
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int N = 5e5+5;

struct node{
	int x,y,v;
	bool operator<(const node &A) const{
		return v<A.v;
	}
}a[N];

int n,m,fa[50001];

int findset(int i){
	if(i==fa[i]){
		return i;
	}
	return fa[i]=findset(fa[i]);
}

void kruskal(){
	for(int i=1;i<=n;i++){
		fa[i]=i;
	}
	sort(a+1,a+m+1);
	int ans=0,cnt=n;
	for(int i=1;i<=m;i++){
		int x=findset(a[i].x),y=findset(a[i].y);
		if(x!=y){
			fa[x]=y;
			ans+=a[i].v;
			cnt--;
		}
	}
	if(cnt==1) cout<<ans<<"\n";
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>a[i].x>>a[i].y>>a[i].v;
	}
	kruskal();

	
	return 0;	

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值