最小生成树

最小生成树

设G(V,E)是无向;联通带权图,对图中每一条边(u,v)的权威c[u][v] , 表示联通u与v的代价,如果G的子图T是一颗包含G的所有顶点的树,则称T为G的生成树,生成树上的权总和成为该生成树的耗费,在G的所有生成树中耗费最小的生成树成为G的最小生成树。
一般来说krukskal算法比较简单且适合稠密图,而prim适合稀疏图

  • krukskal算法
    算法实现:首先将G的n个顶点看成是n个孤立的连通分量,将所有边的权值从小到大排序,然后从第一条边开始,依边递增查看,若看到第i条边(u,v)时,如果端点属于不同的连通分量中的顶点时(用到并查集),就用当前的边(u,v)将其连接起来,当只剩下一个联通分量的时候,该连通分量就为G的一颗最小生成树。
    模板:
#include <cstdio>
#include <algorithm>
using namespace std ;
const int N = 2e5 + 5 ; 
int s[N] ; 
struct node {
	int x , y , w ;
};
node egde[N] ;
bool cmp(node a , node b){
	return a.w < b.w ;
}
int find(int x){
	if (x != s[x])
		s[x] = find(s[x]) ; 
	return s[x] ; 
}
int main(){
	int n , m ;
	scanf ("%d%d",&n,&m) ; 
	for (int i = 1 ; i <= n ; ++ i)
		s[i] = i ; 
	for (int i = 1 ; i <= m ; ++ i)
		scanf ("%d%d%d",&egde[i].x,&egde[i].y,&egde[i].w) ; 
	sort(egde+1,egde+1+m,cmp) ;
	int cost = 0 ; 
	for (int i = 1 ; i <= m ; ++ i){
		int a = find(egde[i].x) , b = find(egde[i].y) ; 
		if (a != b){
			s[a] = b ; 
			n -- ; 
			cost += egde[i].w ; 
		}
		if (n == 1){
			printf ("%d\n",cost) ; 
			return 0 ; 
		}
	} 
	if (n > 1)
		printf ("orz\n") ;
	return 0 ; 
} 

prim算法

详解移步大佬:prim解说
主要就是从未选择的点中选择权值最小的边所对的点加入生成树,直到全部点都加入了生存树。

#include <cstdio>
#include <vector>
using namespace std ;
const int N = 5005 ; 
const int INF = 1e6 ;  
int low[N] , graph[N][N] ;
bool done[N] ; 
int n , m ;
void prim(int u){	//起点u 
	int sum = 0 ;
	for (int i = 1 ; i <= n ; ++ i){
		low[i] = graph[u][i] ; 	//寻找与起点u 相连的边的权值 
		done[i] = false ; 
	}
	done[u] = true ; 
	int j = 1 ; 
	for (int i = 1 ; i < n ; ++ i){	//处理其他n-1个顶点
		int min = INF ; 
		//在low数组中找未加入生成树的最小值 
		for (int k = 1 ; k <= n ; ++ k) {
			if ((low[k] < min) && (!done[k])){
				min = low[k] ; 
				j = k ; 
			}
		}
		//加入结点j
		done[j] = true ;
		sum += low[j] ;  
		for (int k = 1 ; k <= n; ++ k){
			if (low[k] > graph[j][k] && (!done[k])){
				low[k] = graph[j][k] ; 	//更新与j相连的权值 
			}
		}  
	}
	printf ("%d\n",sum) ;
}
int main(){
	scanf ("%d%d",&n,&m) ;
	for (int i = 1 ; i <= n ; ++ i){	//初始化 
		for (int j = 1 ; j <= m ;++ j){
			if (i == j)		graph[i][j] = 0 ; 
			else	graph[i][j] = INF ; 
		}
	} 
	while(m--){		//建图 
		int u , v , w ; 
		scanf ("%d%d%d",&u,&v,&w) ;
		//路径重复时选择权值小的保存 
		graph[u][v] = graph[u][v] < w ? graph[u][v] : w ; 
		graph[v][u] = graph[u][v] ;  
	}
	prim(1) ;
	return 0 ;
} 

不过以上的prim做法时间复杂度较大,可以用堆优化降低时间复杂度

#include <cstdio>
#include <vector>
#include <queue>
using namespace std ; 
const int N = 5005 ;
const int INF = 1e6 ; 
struct edge{
	int to , w ; 
	edge(int a , int b){to = a ; w = b ;} ; 
};
struct node{
	int id , dis ; 
	node(int a , int b){id = a ; dis = b ;} ;
	bool operator < (const node &c)
	const {
		return dis > c.dis ;  
	} 
};
int d[N] ;
bool done[N] ;  
int n , m , cnt ; 
vector<edge> e[N] ; 
void prim(int u){	//起点 
	int sum = 0  ;
	for (int i = 1 ; i <= n ; ++ i){
		d[i] = INF ; 
		done[i] = false ; 
	}
		
	priority_queue<node> q ; 
	d[u] = 0 ; 
	q.push(node(u,0)) ; 
	while(!q.empty() && cnt<n){
		int head = q.top().id ; 
		q.pop() ; 
		if (done[head])	   continue ; 
		done[head] = true ; 
		++ cnt ;
		sum += d[head] ; 
		for (int i = 0 ; i < e[head].size() ; ++ i){
			edge t = e[head][i] ; 
			if (done[t.to])	continue ; 
			if (d[t.to] > t.w){
				d[t.to] = t.w ;
				q.push(node(t.to,d[t.to])) ; 
			}
		}
	}
	if (cnt == n)	printf ("%d\n",sum) ;
	else	printf ("orz\n") ;
}
int main(){
	scanf ("%d%d",&n,&m) ;
	while(m --){
		int u , v , w ; 
		scanf ("%d%d%d",&u,&v,&w) ;
		e[u].push_back(edge(v,w)) ; 
		e[v].push_back(edge(u,w)) ; 
	}
	prim(1) ;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值