最小生成树
1 最小生成树的概念
最小生成树(Minimum Spanning Tree,简称MST)指一个联通无向图中包含所有顶点的一棵树(也就是没有环),且该树所有边的边权最小。
例如,对于以下的图:
最小生成树(用红线标记)为:
2 求解最小生成树
最小生成树模板题
最小生成树主要有两种算法:Prim算法和Kruskal算法。
2.1 Prim算法
2.1.1 求解过程
个人认为,Prim算法与最短路中的Dijkstra算法很像。
我们需要维护一个数组 \(dis\),表示已用点到未用点的最短距离,记得全部初始化为 \(\infty\)。每次我们选择任意节点,遍历节点 \(1\sim n\),选出最小值(是不是跟Dijkstra很像)并将这个节点加入最小生成树,更新 \(ans\),然后更新 \(dis\) 数组。
时间复杂度:\(O(n^2+m)\)(其实还能使用优先队列优化,这里就不讲了)
2.1.2 代码
#include<bits/stdc++.h>
using namespace std;
struct node{
int v;
int w;
};
vector<node> p[114514];
int n,m,ans;
int dis[114514];
bool flag[114514];//flag数组用来判断该节点是否加入最小生成树
void prim(){
dis[1]=0;
for(int i=1;i<=n;i++){
int minn=INT_MAX;
int pos=-1;
for(int j=1;j<=n;j++)
if(!flag[j]&&dis[j]<minn)
minn=dis[j],pos=j;//选最小值
if(pos==-1){
ans=INT_MAX;
break;
}
flag[pos]=1;//将pos点加入最小生成树
ans+=minn;//更新ans
for(int j=0;j<p[pos].size();j++){
int v=p[pos][j].v;
int w=p[pos][j].w;
if(!flag[v]&&w<dis[v]) dis[v]=w;//更新dis数组
}
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) dis[i]=INT_MAX;
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
p[u].push_back({v,w});
p[v].push_back({u,w});
}
prim();
if(ans!=INT_MAX) cout<<ans;
else cout<<"orz";
return 0;
}
2.2 Kruskal算法
前置知识:并查集。
2.2.1 Kruskal算法求解过程
由于Kruskal算法需要并查集来维护合并,于是先写两个并查集函数:
int find(int x){
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}//查找“祖先”(使用路径压缩)
void join(int c1,int c2){
int x=find(c1),y=find(c2);
if(x!=y) fa[x]=y;
}//合并集合
我们还需要一个变量 \(ltk\) 表示当前联通块个数,初始化为 \(n\),每次合并集合则 \(ltk--\),当 \(ltk=1\) 时,代表已经求出最小生成树。
首先,需要将节点按边权从小到大排序,保证每次选择的边一定是当前没被选中中最短的。然后循环 \(m\) 次来求解最小生成树,如果当前边的两个节点不在同一个集合中(由于这两个节点不在同一集合,就不会出现环,两个节点在同一集合合并才会出现环),则合并。
2.2.2 代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int fa[314514];
int n,m,ltk,ans;
struct node{
int u,v;
int w;
}a[314514];
bool cmp(node a,node b){
return a.w<b.w;
}//比较函数
int find(int x){
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}//查找“祖先”(路径压缩)
void join(int c1,int c2){
int x=find(c1),y=find(c2);
if(x!=y) fa[x]=y,ltk--;
}//合并集合
int Kruskal(){
for(int i=1;i<=m;i++){
if(find(a[i].u)!=find(a[i].v)){
join(a[i].u,a[i].v);
ans+=a[i].w;
}//判断并合并集合
if(ltk==1) return ans;
}
return -1;
}
signed main(){
cin>>n>>m;
ltk=n;
for(int i=1;i<=n;i++) fa[i]=i;//并查集初始化
for(int i=1;i<=m;i++) cin>>a[i].u>>a[i].v>>a[i].w;
sort(a+1,a+m+1,cmp);//排序
if(Kruskal()!=-1) cout<<ans;
else cout<<"orz";
return 0;
}

浙公网安备 33010602011771号