MST(最小生成树)

一、什么是图的最小生成树(MST)

  • N个点用N-1条边连接成一个连通块,形成的图形只可能是树,没有别的可能。

  • 一个有N个点的图,边一定是大于等于N-1条的。图的最小生成树,就是在这些边中选择N-1条出来,连接所有的N个点。这N-1条边的边权之和是所有方案中最小的。

二、最小生成树用来解决什么问题?

就是用来解决如何用最小的“代价”用N-1条边连接N个点的问题。

三、Prim算法

  • Prim算法采用与Dijkstra、Bellman-Ford算法一样的“蓝白点”思想:白点代表已经进入最小生成树的点,蓝点代表未进入最小生成树的点。


算法描述:(时间复杂度n的2次方(邻接矩阵),邻接表O(E log(n))其中E为边数)
以1为起点生成最小生成树,min[v]表示蓝点v与白点相连的最小边权,MST表示最小生成树的权值之和


伪代码

a)初始化:min[v]=(v≠1); min[1]=0;MST=0;
b)for (i = 1; i<= n; i++)
    1.寻找min[u]最小的蓝点u。
    2.将u标记为白点
    3.MST+=min[u]
    4.for 与白点u相连的所有蓝点v  
      if (w[u][v]<min[v]) 
          min[v]=w[u][v];
c)算法结束: MST即为最小生成树的权值之和

堆优化code

int Prim(int st){
	priority_queue<node>q;
	for(int i=1;i<=n;++i)d[i]=0x3f3f3f3f;
	q.push((node){st,0});
	d[st]=0;
	int ans=0;
	while(!q.empty()){
		node x=q.top();
		q.pop();int id=x.id;
		if(!vis[id]){
			vis[id]=1;
			ans+=d[id];
			for(int i=head[id];i;i=e[i].next){
				int v=e[i].to;
				d[v]=min(d[v],e[i].dis);
				if(!vis[v]){
					q.push((node){v,d[v]});
				}
			}
		}
	}
	return ans;
}
  • 注意,这里与dijistra的区别。dijistra中min[v]表示v与起点的距离,这里表示与最近的蓝点的距离

简单证明

反证法

假设权值最小的边不在最小生成树中。
此时将权值最小的边加入生成树中,那么必然会构成一 个回路(最小生成树有n个点,最小边加入之后会有n条边),任意去掉环里比权值最小的边权值大的一条边,这样就构造了更优的一个解,这时与假设构成矛盾。所以权值最小的边一定在最小生成树中。

四、Kruskal

- Kruskal算法每次从剩下的边中选择一条不会产生环路的具有最小耗费的边加入已选择的边的集合中。注意到所选取的边若产生环路则不可能形成一棵生成树。

Kruskal算法分e 步,其中e 是网络中边的数目。按耗费递增的顺序来考虑这e 条边,每次考虑一条边。当考虑某条边时,若将其加入到已选边的集合中会出现环路,则将其抛弃,否则,将它选入。
用并查集维护在同一棵树上的点,如果边上的两点已经在一棵树上,加边后一定会有环。

Kruskal算法在连边的时候实际上是把两个连通块相连,如果图本身连通则运行完之后应该只有一个连通块

int liantongkuai=n;//一个点就是一个连通块
int Kruscal(){
	for(int i=1;i<=n;++i)f[i]=i;
	sort(e+1,e+k+1,cmp);
	int m=0,ans=0;
	for(int i=1;i<=k;++i){
		if(m==n-1)break;
		int x=e[i].from,y=e[i].to;
		if(find(x)!=find(y)){
			ans+=e[i].dis;m++;Union(x,y);liantongkuai--;
		}
	}
	return ans;
}
posted @   Chano_sb  阅读(524)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示