贪心-最小生成树-Prime

最小生成树(MST):是在一个给定的无向图G(V,E)中求一棵树T,使得这棵树拥有图G 中的所有顶点,且所有边是来自图G中的边,并且满足整棵树的边权之和最小。
性质:
①最小生成树是树,因此其边数等于顶点数减1,且树内一定不会有环
②对给定的图G(V,E),其最小生成树可以不唯一,但其边权之和一定是唯一的
③由于最小生成树是在无向图上生成的,因此其根节点可以是这棵树上的任意一个结点

Prime算法基本思想:
对图G(V,E)设置集合S,存放已被访问的顶点,然后每次从集合V-S选择与集合S的最短距离最小的一个顶点(记为u),访问并加入集合S。

在这里插入图片描述
Code:

#include<iostream>
#include<algorithm>
#include<vector>
#include<numeric>
using namespace std;
int N,M;
struct T{
	int to,w;//下一个节点  权值 
};
vector<vector<T>>Edge;//存储边的信息及权值 
vector<int>d;//存储每个点的 
vector<bool>used;//是否被使用过 
int ans=0;//记录权值之和 
void Prime(){
	for(int i=0;i<=N;i++)//把所有节点到已有生成树的距离为无穷大 
		d[i]=0x3f3f3f3f;
	d[1]=0;//开始点为0 
	while(true){
		int x=-1;
		for(int i=1;i<=N;i++)
			if(!used[i]&&(x==-1||d[i]<d[x]))//找出未被访问  且是生成树外的最小节点 加入生成树中  
				x=i;
		if(x==-1)//图里没有小的了  如果有 则是不连通图  再对其它连通片执行Prim()算法 得出来就是此图的最小生成森林 
			break; 
		cout<<x<<" "<<d[x]<<endl;
		used[x]=true;//标记为已访问 
		ans+=d[x];//记录权值 
		for(auto &e:Edge[x])//e是一个结构体 e.to是与x相邻接的边  e.w为边上的权值 
			d[e.to]=min(d[e.to],e.w); //更新x所有连接的边   找出自身已更新的值d[e.to]和边权值[e.w]中小的 
	}
}
int main(){
	cin>>N>>M;//顶点数  边数 
	Edge.resize(N+1);//节点1~N 
	d.resize(N+1);
	used.resize(N+1);
	int x,y,w;
	for(int i=1;i<=M;i++){//无向图 
		cin>>x>>y>>w;
		Edge[x].push_back({y,w});
		Edge[y].push_back({x,w});
	}
	cout<<"最小生成树:"<<endl;
	Prime();
	cout<<"权值之和: ";
	cout<<ans;//输出最小生成树的权值之和 
	return 0;
} 
//6 10
//1 2 4
//1 5 1
//1 6 2
//2 3 6
//2 6 3
//3 6 5
//3 4 6
//4 6 5
//4 5 4
//5 6 3


注意:使用这种写法复杂度是O(N^2),可以利用堆优化为O(NlogN)

利用STL中的优先队列
头文件:< queue >
基本操作
empty() 如果队列为空返回真
pop() 删除对顶元素
push() 加入一个元素
size() 返回优先队列中拥有的元素个数
top() 返回优先队列对顶元素
声明方式
①priority_queue< int >q;
//通过操作,按照元素从大到小的顺序出队
②priority_queue<int, vector, greater< int > q;
//此时整数中元素小的优先级高。

Code:

#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<numeric>
using namespace std;
using P=pair<int,int>;
int N,M;
struct T{
	int to,w;//下一个节点  权值 
};
vector<vector<T>>Edge;//存储边的信息及权值 
priority_queue<P,vector<P>,greater<P> >Q;
vector<int>d;
vector<bool>used;//是否被使用过 
int ans=0;//记录权值之和 
void Prime(){
	for(int i=0;i<=N;i++)//把所有节点到已有生成树的距离为无穷大 
		d[i]=0x3f3f3f3f;
	d[1]=0;
	Q.push({0,1});//把1结点加入 并且d[1]为0 
	while(!Q.empty()){
		auto e=Q.top();//取出队头元素  根据小顶堆  第一个为最小的 
		Q.pop();//弹出队列 
		int x=e.second,w=e.first;
		if(used[x])//若x被访问过  则继续弹出下一个进行访问 
			continue;
		ans+=d[x];
		used[x]=true;//标记为已访问 
		cout<<x<<" : "<<d[x]<<endl; 
		for(auto h:Edge[x]){
			if(d[h.to]>h.w){//d值 比边权值大 则更新 
				d[h.to]=h.w;
				Q.push({h.w,h.to});//放入优先队列中 
			}
		}
	}
	
	
}
int main(){
	cin>>N>>M;//顶点数  边数 
	Edge.resize(N+1);//节点1~N 
	d.resize(N+1);
	used.resize(N+1);
	int x,y,w;
	for(int i=1;i<=M;i++){//无向图 
		cin>>x>>y>>w;
		Edge[x].push_back({y,w});
		Edge[y].push_back({x,w});
	}
	cout<<"最小生成树:"<<endl;
	Prime();
	cout<<"权值之和: ";
	cout<<ans;//输出最小生成树的权值之和 
	return 0;
} 
//6 10
//1 2 4
//1 5 1
//1 6 2
//2 3 6
//2 6 3
//3 6 5
//3 4 6
//4 6 5
//4 5 4
//5 6 3


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值