图--最小生成树

什么是最小生成树

  • 是一棵树
    无回路
    |V|个顶点一定有 |V|-1 条边
  • 是生成树
    不唯一
    包含全部顶点
    |V|-1 条边都在图里
  • 边的权值和最小

举个列子,有6个村落,如何修路能使6个村落都相通,同时花费最少呢?

一第一种算法:Prim算法

即从一个根节点开始,慢慢的收录别的节点,dist[i]表示该节点离生成树的最近距离
从v1->v4
v1->v2>v4
v1->v2->v4->v3
v1->v2->v4->v3->v7->v6->v5
在这里插入图片描述

void Prim(){
    MST = {s};  // parent[s] = -1
    while(1){
        V = 未收录顶点中dist最小者;   // dist[V] = E<V,W> 或 正无穷
        if ( 这样的V不存在 )
            break;
        dist[V] = 0;  // 将V收录进MST
        for ( V 的每个邻接点 W )
            if ( dist[W]!= 0)
                if ( E<V,W> < dist[w] ){
                    dist[W] = E<V,W>;
                    parent[W] = V;
                }
    }
    if ( MST 中收的顶点不到|V|)
        Error ( "图不连通" );
}

其中dist[i]表示该点到数的最短距离,如果能被数收录,就让dist[i]=0,目的是防止形成回路
代码如下:

#include<bits/stdc++.h>
using namespace std;
int dist[1000]={0};
int G[1000][1000]={0};
int parent[1000]={0};//存放并查集的根节点 
int n,m;
int sum=0;
vector<int>ve;//存放收录的顺序 
int cnt=0;
void create(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		dist[i]=100000;
		parent[i]=i;
	} 
	for(int i=1;i<=m;i++){
		int l1,l2,v;
		cin>>l1>>l2>>v;
		G[l1][l2]=v;
		G[l2][l1]=v;
	}
}
void init(int s){
	dist[s]=0;
	ve.push_back(s);
	for(int i=1;i<=n;i++){
		if(i!=s&&G[s][i]){
			dist[i]=G[s][i];
			parent[i]=s;
		}
	}
}
int findmin(int s){
	int mini=-1;
	int m=100000;
	for(int i=1;i<=n;i++){
		if(i!=s&&dist[i]&&dist[i]<m){
			m=dist[i];
			mini=i;
		}
	}
	return mini;
}
void prim(int s){
	init(s);
	while(true){
		int i=findmin(s);
		if(i==-1)break;
		sum+=dist[i];
		cnt++;
		dist[i]=0;
		ve.push_back(i);
		for(int j=1;j<=n;j++){
			if(dist[j]&&G[i][j]){
				if(G[i][j]<dist[j]){
					dist[j]=G[i][j];
					parent[j]=i;
				}
			}
		}
	}
}
void output(){
	cout<<"收录的顺序为:"<<endl; 
	for(int i=0;i<=cnt;i++){
		 cout<<ve[i]<<" ";
	}
	cout<<"权重和:"<<sum<<endl;
	cout<<"该生成树为:"; 
	for(int i=1;i<=n;i++){
		cout<<parent[i]<<" ";
	} 
	
}
int main(){
	create();
	prim(1);
	output();
}
/*
7 12
1 2 2
1 3 4
1 4 1
2 4 3
2 5 10
3 4 2
3 6 5
4 5 7
4 7 4
4 6 8
5 7 6
6 3 5
6 7 1


结果:
被收录顺序:
4 2 3 7 6 5 45702 权重和为:20
该生成树为:
-1 1 4 1 7 3 4 

*/

二第二种算法:cruskal算法

即每次收录最小的边,分别形成一颗颗树,最后去合并这些树,生成最小生成树

  • 如何保证不会形成回路?
    用并查集表示父节点,这样两个点只要父节点不相同,就可以合并
  • 如何选取最小的边?
    利用最小堆,如果要用stl的话,要重载运算符
#include<bits/stdc++.h>
using namespace std;
struct node{
	int v1;
	int v2;
	int w;
	bool operator < (const node & n)const{
	    return w>n.w;
	}
};
priority_queue<node>q;//最小堆 
vector<node>v;//存放收录的顺序 
int parent[1000]={0};
int n,m;
int sum=0;
void create(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		parent[i]=-1;//并查集初始化 
	}
	for(int i=0;i<m;i++){
		node n;
		int v1,v2,w;
		cin>>v1>>v2>>w;
		n.v1=v1;
		n.v2=v2;
		n.w=w;//把每一条边推进最小堆 
		q.push(n);
	}
}
int find(int x){
	if(parent[x]<0)return x;
	else return parent[x]=find(parent[x]);
} 
void Union(int x,int y){//按孩子个数归并,parent[]表示该根节点拥有的孩子个数 
	int l1=find(x);
	int l2=find(y);
	if(l1<l2){
		parent[l1]+=parent[l2];
		parent[l2]=l1;
	}else{
		parent[l2]+=parent[l1];
		parent[l1]=l2;
	}
}
void kruska(){
       while(!q.empty()&&v.size()!=n-1){
		node n=q.top();
		q.pop();
		if(find(n.v1)!=find(n.v2)){
			  v.push_back(n);
		      sum+=n.w;
		      Union(n.v1,n.v2);
		}
	}
}
void output(){
	cout<<"权重和:"<<sum<<endl;
	cout<<"收录的顺序为:"<<endl; 
	for(int i=0;i<n;i++){
		 cout<<v[i].w<<" ";
	}
	cout<<"该生成树为:"; 
	for(int i=1;i<=n;i++){
		cout<<parent[i]<<" ";
	} 
	
}
int main(){
	create();
	kruska();
	output();
}




练习题

在这里插入图片描述

6 15
1 2 5
1 3 3
1 4 7
1 5 4
1 6 2
2 3 4
2 4 6
2 5 2
2 6 6
3 4 6
3 5 1
3 6 1
4 5 10
4 6 8
5 6 3

12

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值