Kruskal算法求最小生成树
给定一个 n 个点 m 条边的无向图,图中可能存在重边和自环,边权可能为负数。
求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible
。
给定一张边带权的无向图 G=(V,E),其中 V 表示图中点的集合,E 表示图中边的集合,n=|V|,m=|E|。
由 V 中的全部 b 个顶点和 E 中 n−1 条边构成的无向连通子图被称为 G 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 G 的最小生成树。
输入格式
第一行包含两个整数 n 和 m。
接下来 m 行,每行包含三个整数 u,v,w,表示点 u 和点 v 之间存在一条权值为 w 的边。
输出格式
共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible
。
数据范围
1≤n≤105,
1≤m≤2∗105,
图中涉及边的边权的绝对值均不超过 1000。
输入样例:
4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4
输出样例:
6
Kruskal算法求最小生成树:
- 维护一个连通块儿集合,代表最小生成树,初始为空。
- 将所有的边按权重从小到大排序。
- 遍历每条边 (
a,b,w: a,b两点之间的边,权重为w
),如果a和b不在连通块儿中(a,b不连通),就将a,b边加入连通块中。 - 直到最后连通块儿中的边数为 n-1,则该图有最小生成树,反之则没有。
代码:C++:
使用最小堆来存储边。
#include<bits/stdc++.h>
using namespace std;
const int N = 100010,INF=0x3f3f3f3f;
typedef pair<int,pair<int,int>> PII;
int n,m,res; //res记录最小生成树的边长和。
priority_queue<PII,vector<PII>,greater<PII> > heap;
//堆中存储着 {权值,{两个点}} {w,{a,b}}
int fa[N],cnt[N];
//fa[]:并查集,记录x的节点的父节点
//cnt[]:记录根节点所在连通块中的点数
int get(int x) //找到x的根节点
{
if(x==fa[x]) return x;
return fa[x]=get(fa[x]);
}
void merge(int x,int y) //连通x,y两点,并更新连通块儿中点的数量。
{
int a=get(x),b=get(y);
fa[b]=a;
cnt[a]+=cnt[b];
}
int kruskal()
{
while(heap.size())
{
auto t=heap.top();
heap.pop();
int c=t.first,a=t.second.first,b=t.second.second;
if(get(a)==get(b)) continue;
res+=c;
merge(a,b);
}
if(cnt[get(1)]!=n) return INF;
return res;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
fa[i]=i;
cnt[i]=1;
} //初始节点的父节点都为自己,连通块儿中点数都为1
while(m--)
{
int x,y,z;
cin>>x>>y>>z;
if(x!=y) heap.push({z,{x,y}});
}
int t=kruskal();
if(t==INF) puts("impossible");
else printf("%d",t);
return 0;
}
使用结构体存储边
#include<bits/stdc++.h>
using namespace std;
const int N = 200010,INF=0x3f3f3f3f;
int n,m,res,cnt;
int fa[N];
struct Edge
{
int a,b,w;
}edges[N];
bool cmp(Edge x,Edge y) //定义规则,快排排序
{
return x.w<y.w;
}
int get(int x)
{
if(x==fa[x]) return x;
return fa[x]=get(fa[x]);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=0;i<m;i++)
{
int x,y,z;
cin>>x>>y>>z;
edges[i]={x,y,z};
}
sort(edges,edges+m,cmp);
for(int i=0;i<m;i++)
{
int a=edges[i].a,b=edges[i].b,w=edges[i].w;
a=get(a),b=get(b); //找到a,b的根
if(a!=b) //如果a,b不连通,加入连通块儿,cnt记录边数,边数+1
{
fa[b]=a;
res+=w;
cnt++;
}
}
if(cnt<n-1) puts("impossible"); //最小生成树边数=点数-1
else cout<<res<<endl;
return 0;
}