なんでバカのブログを読みたいの!为什么要看菜鸟的博客!|

园龄:粉丝:关注:

最小生成树 笔记

一、最小生成树是什么

  • 生成树,由一个连通图的所有顶点和其中若干条边构成的树。

  • 最小生成树,指一个连通图的所有生成树,边权之和最小的一个。

二、实现方法

1. Prim

(1). 流程

Prim 算法的流程如下:

  1. 初始时从图中取一节点加入树。

  2. 选择一个与当前树中顶点距离最近的顶点,将该顶点和相应的边加入树。

  3. 更新每个顶点离该顶点的距离。

  4. 当所有的顶点都在树中时,这棵树就是最小生成树。

(2). 时间复杂度

Prim 和 Dijkstra 有异曲同工之妙,时间复杂度的瓶颈在第二步:选距离最近的顶点。直接使用循环枚举这一步时,时间复杂度为 $O(n^2)$,但是,可以用优先队列来优化这一部分的耗时。因此,Prim 最终的时间复杂度是 $O(n\log n)$。

(3). 代码实现

#include<bits/stdc++.h>
#define int long long
#define pii pair <int,int>
using namespace std;
void fread(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
const int N=2e5+10,inf=0x7fffffff;
int n,m,head[N],idx,dis[N],cnt,ans;
bool vis[N];
struct EDGE{
int v,w,next;
}edge[N*2];
void add(int u,int v,int w){
edge[++idx]=(EDGE){v,w,head[u]};
head[u]=idx;
}
void prim(){
fill(dis+2,dis+N,inf);
priority_queue<pii,vector<pii>,greater<pii>>q;
q.push({0,1});
while(cnt<n and !q.empty()){
pii t=q.top();
q.pop();
int dist=t.first;
int u=t.second;
if(vis[u])continue;
vis[u]=true;
cnt++;
ans+=dist;
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].v,w=edge[i].w;
if(!vis[v] and w<dis[v]){
dis[v]=w;
q.push({w,v});
}
}
}
}
signed main(){
fread();
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%lld%lld%lld",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
prim();
if(cnt!=n)cout<<"impossible";
else cout<<ans;
return 0;
}

2. Kruskal

(1). 流程

Kruskal 极为通俗易懂,算法流程如下:

  1. 将所有边按边权从小到大排序,由于要存储边的起点和终点,因此不再选用链式前向星,而是用与 Bellman-ford 相同的存图方式。

  2. 将边从小到大选,如果选择了当前这一条边之后,不会成为一张图(不会出现环),就将这一条边添加至树中。可以选用并查集来判断是否有环。

  3. 当选够 $n-1$ 条边时,树就构成了,此时得到的树就是最小生成树。

(2). 时间复杂度

Kruskal 的时间复杂度的瓶颈在第一步排序,因此 Kruskal 的时间复杂度是 $O(m\log m)$

关于 Prim 和 Kruskal 选用谁更好,那就要比较 $n$ 与 $m$ 谁大,边比点多时用 Prim,点比边多时用 Kruskal。

(3). 代码实现

#include<bits/stdc++.h>
#define int long long
#define pii pair <int,int>
using namespace std;
void fread(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
const int N=2e5+10,inf=0x7fffffff;
int n,m,dis[N],cnt,ans,f[N];
bool vis[N];
struct EDGE{
int u,v,w;
}edge[N*2];
bool cmp(EDGE x,EDGE y){
return x.w<y.w;
}
int gf(int x){
if(f[x]!=x)f[x]=gf(f[x]);
return f[x];
}
void kruskal(){
for(int i=1;i<=n;i++){
f[i]=i;
}
for(int i=1;i<=m;i++){
int u=edge[i].u;
int v=edge[i].v;
int w=edge[i].w;
int fu=gf(u);
int fv=gf(v);
if(fu!=fv){
f[fu]=fv;
ans+=w;
cnt++;
}
}
}
signed main(){
fread();
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%lld%lld%lld",&u,&v,&w);
edge[i]=(EDGE){u,v,w};
}
sort(edge+1,edge+m+1,cmp);
kruskal();
if(cnt!=n-1)cout<<"impossible";
else cout<<ans;
return 0;
}

本文作者:Garbage fish's Blog

本文链接:https://www.cnblogs.com/Garbage-fish/p/17825001.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Garbage_fish  阅读(16)  评论(0编辑  收藏  举报  
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起