最小生成树(模板)
题目
这是个模板题
链接:P3366 【模板】最小生成树
题目描述
如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz。
输入格式
第一行包含两个整数 $ N,M $ ,表示该图共有 $ N $ 个结点和 $ M $ 条无向边。
接下来 $ M $ 行每行包含三个整数 $ X_i,Y_i,Z_i $ ,示有一条长度为 $ Z_i $ 的无向边连接结点 $ X_i,Y_i $。
输出格式
如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz。
数据规模
对于 20% 的数据,$ N≤5,M≤20 $ 。
对于 40% 的数据,$ N≤50,M≤2500 $ 。
对于 70% 的数据,$ N≤500,M≤10 $ 。
对于 100%100% 的数据:$ 50001≤N≤5000, 1≤M≤2×10^5 $ 。
题解
Kruskal 算法的主要思路:
-
先以边的权值来给边排序,从小到大
-
然后开始由小到大遍历边,也就是一步一步的点的扩展
-
然后判断这个点的是否在已拓展的点内,如果不在就将答案加上它的边权,并且加入并查集
注意一下,这个是用邻接表edge写的(u,v为边的两个点,为边权),没有用领接矩阵
基本上这个模板就这样了,代码如下:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
struct edge{
int u,v,w; //定义一个边的结构体,u、v为边的两个定点,w为边的边权
};
edge e[200005];
int father[5005]; //并查集
int ans = 0;
bool cmp(const edge& a,const edge& b){
return a.w < b.w; //sort()的判断,以边权的由大到小的顺序来排序
}
int found(int x){ //并查集的寻找父亲
return father[x] == x ? x : father[x] = found(father[x]); //压行,并且压缩路径,这样可以将之后的查找时间变为常数级
}
int main()
{
int n,m;
cin>>n>>m;
for(int i = 1;i <= m;i++)
{
scanf("%d %d %d",&e[i].u,&e[i].v,&e[i].w);
}
sort(e,e+m,cmp); //kruskal 算法的重要部分:按照边权的大小来排序
for(int j = 1;j<=n;j++) father[j] = j; //初始化,每一个点都是一个自己的父亲
int k = 0;
for(int j = 1;j<=m;j++)
{
if(k == n - 1) break;
int v = e[j].v,u = e[j].u;
if(found(v) != found(u)) //如果没有这个边所在的点没有加入,就执行下面
{
k++; //注意一点:这一句话一定要在这里写,我已经掉过坑里了
ans += e[j].w; //加入答案:最小生成树的总长
int f = found(v),t = found(u);
father[t] = f; //使这个的边的这个点加入路径
}
}
int cnt = 0;
for(int i = 1;i<=n;i++)
if(father[i] == i)
cnt++;
if(cnt == 1)
cout<<ans<<endl;
else
cout<<"orz"<<endl; //如果有边不联通,就输出“orz”
getchar();getchar();
return 0;
}