最小生成树

下面介绍两种求最小生成树算法

1.Kruskal算法

此算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。 
1. 把图中的所有边按代价从小到大排序; 
2. 把图中的n个顶点看成独立的n棵树组成的森林; 
3. 按权值从小到大选择边,所选的边连接的两个顶点ui,viui,vi,应属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。 
4. 重复(3),直到所有顶点都在一颗树内或者有n-1条边为止。

例题:洛谷P1195

题目背景

小杉坐在教室里,透过口袋一样的窗户看口袋一样的天空。

有很多云飘在那里,看起来很漂亮,小杉想摘下那样美的几朵云,做成棉花糖。

题目描述

给你云朵的个数NNN,再给你MMM个关系,表示哪些云朵可以连在一起。

现在小杉要把所有云朵连成KKK个棉花糖,一个棉花糖最少要用掉一朵云,小杉想知道他怎么连,花费的代价最小。

输入格式

每组测试数据的

第一行有三个数N,M,K(1≤N≤1000,1≤M≤10000,1≤K≤10)N,M,K(1 \le N \le 1000,1 \le M \le 10000,1 \le K \le 10)N,M,K(1≤N≤1000,1≤M≤10000,1≤K≤10)

接下来MMM行每行三个数X,Y,LX,Y,LX,Y,L,表示XXX云和YYY云可以通过LLL的代价连在一起。(1≤X,Y≤N,0≤L<10000)(1 \le X,Y \le N,0 \le L<10000)(1≤X,Y≤N,0≤L<10000)

30%30\%30%的数据N≤100,M≤1000N \le 100,M \le 1000N≤100,M≤1000

输出格式

对每组数据输出一行,仅有一个整数,表示最小的代价。

如果怎么连都连不出KKK个棉花糖,请输出'No Answer'。

输入输出样例

输入 #1

3 1 2
1 2 1

输出 #1

1

代码:

#include<bits/stdc++.h>
using namespace std;
const int num=200005;
struct data{		//结构体存图,start到end需要花费value 
	int start,end,value;
}a[num];
int f[num];		//并查集数组 
bool cmp(data a,data b){	//按费用从低到高排序 
	return a.value<b.value;
}
int find(int a){		//并查集,用于判断是否成环 
	if(f[a]==a){
		return a;
	}
	else{
		return f[a]=find(f[a]);
	}
}
int main(){
	int n,m,k;
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++){
		f[i]=i;
	}
	for(int i=1;i<=m;i++){
		cin>>a[i].start>>a[i].end>>a[i].value;
	}
	sort(a+1,a+m+1,cmp);
	int cnt=0,sum=0;
	for(int i=1;i<=m;i++){
		if(find(a[i].start)!=find(a[i].end)){	//父节点不一样证明连接起来不会成环 
			f[find(a[i].start)]=find(a[i].end);	//把这两个点归为一个父节点 
			sum+=a[i].value;
			cnt++;
		}
		if(cnt>=n-k) break;		//已经连接了n-k条边,组成k个树 
	}
	if(cnt>=n-k){
		cout<<sum<<endl;
	}
	else cout<<"No Answer"<<endl;
	return 0;
}

2.Prim算法

此算法可以称为“加点法”,每次迭代选择代价最小的边对应的点,加入到最小生成树中。算法从某一个顶点s开始,逐渐长大覆盖整个连通网的所有顶点。

  1. 图的所有顶点集合为VV;初始令集合u={s},v=V−uu={s},v=V−u;
  2. 在两个集合u,vu,v能够组成的边中,选择一条代价最小的边(u0,v0)(u0,v0),加入到最小生成树中,并把v0v0并入到集合u中。
  3. 重复上述步骤,直到最小生成树有n-1条边或者n个顶点为止。

由于不断向集合u中加点,所以最小代价边必须同步更新;需要建立一个辅助数组closedge,用来维护集合v中每个顶点与集合u中最小代价边信息;


struct{
  char vertexData   //表示u中顶点信息
  UINT lowestcost   //最小代价
}closedge[vexCounts]

然后存一个自己写的Prim代码:

#include<bits/stdc++.h>
using namespace std;
const int MAX=0x3f3f3f3f;
int edge[1111][1111];		//用来存放两点间的权值 
int lowcost[200005];		//用来存放每个点距离Vnew的最短距离 
int vis[200005];			//标记,如果为0则是V集合的点,为1则是Vnew集合里的点 
int main() {
	int n,m,sum=0;			//n个点,m条边,sum为最小生成树权值总和 
	memset(edge,MAX,sizeof(edge));
	cin>>n>>m;
	for(int i=1; i<=m; i++) {
		int a,b,c;
		cin>>a>>b>>c;
		edge[a][b]=c;
		edge[b][a]=c;
	}
	for(int i=1; i<=n; i++) {		//我们把1点当作起始点,更新起始点到每个点的权值 
		lowcost[i]=edge[1][i];
	}
	vis[1]=1;						//把1点放入Vnew集合,此时Vnew={1},V={其他点} 
	for(int i=1; i<n; i++) {
		int MIN=MAX;
		int v=0;					//用来标记每次筛选出距离集合Vnew里最近的点 
		for(int j=1; j<=n; j++) {
			if(!vis[j]&&lowcost[j]<MIN) {
				MIN=lowcost[j];
				v=j;
			}
		}
		if(v) {
			vis[v]=1;				//因为把这个点v放入了Vnew,所以vis[v]=1 
			sum+=lowcost[v];		//加上该点构成树的权值 
			for(int j=1; j<=n; j++) {		//依次将V集合里的点的距离Vnew集合的最小权值 
				if(!vis[j]&&edge[v][j]<lowcost[j]) { 
					lowcost[j]=edge[v][j];
				}
			}
		}
	}
	cout<<sum<<endl;
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值