题目:
![](https://img-blog.csdnimg.cn/d676e9feac9f4fa1917bb8b31e6e655c.png)
题解涉及到的知识点:
在连通网中查找最小生成树的常用方法有两个,分别称为普里姆算法和克鲁斯卡尔算法。本文章,将讲解克鲁斯卡尔算法和其例题(怎样用它写最小生成树)。
克鲁斯卡尔算法查找最小生成树的方法是:将连通网中所有的边按照权值大小做升序排序,从权值最小的边开始选择,只要此边不和已选择的边一起构成环路,就可以选择它组成最小生成树。对于 N 个顶点的连通网,挑选出 N-1 条符合条件的边,这些边组成的生成树就是最小生成树。
题解:
上述题目的思路:
1.输入边,用结构体储存
2.用结构体快排以边比较从小到大快排
3.建一个并查集,并初始化并查集(并查集代表两个点有没有在同一个树里面)
4.进行kruskal算法:当到了已连边的个数是点的个数-1时,就要停止循环,因为这个时候,最小生成树已经完成了,所有的并查集都连在了一起。
上述题目的代码如下
#include<iostream>
#include<algorithm> //用到排列的要用这个头文件
using namespace std;
int n,m,u,v,total;
struct edge{ //用结构体储存边
int start,to;long long val; //分别定义边的开头,边的结尾和边的长度
}bian[200005];
long long ans;
int f[5005]; //为(并查集)准备的数组
bool cmp(edge a,edge b) //边的快速排序的定义
{
return a.val<b.val; //从小到大排序
}
int find(int x) //并查集部分,作用是:判断有没有连成一个环。
{
if(f[x]==x) return x; //f[i]表示x的“上级”,即x的父亲
return find(f[x]); //此处省略了else,原来应该是else return find(f[x]);
}
inline void kruskal() //最小生成树的kruskal算法
{
for(int i=1;i<=m;i++) //m表示边
{
u=find(bian[i].start); //边的开头 的上级
v=find(bian[i].to); //边的结尾的上级 ,
//目的是为了看边的开头和结尾是否在一个并查集里,他们有没有形成一个环
if(u==v) continue; /*if()continue语句的作用是:
如果满足if里面的条件,则不执行循环语句里面的剩余内容,跳出循环,执行下一次循环*/
ans+=bian[i].val; //如果不在同个并查集里面,就边的长度(权值)的叠加
f[u]=v; //连接两个并查集
total++; //边++;
if(total==n-1)break; //点-1=边,即形成了最小生成树,则退出(之后做的也没用了)
}
if(total==n-1) //如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。
{
cout<<ans<<endl;
}
else cout<<"orz"<<endl; //如果该图不连通则输出 orz。
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
f[i]=i; //初始化结点的上级就是它本身;
for(int i=1;i<=m;i++)
cin>>bian[i].start>>bian[i].to>>bian[i].val; //输入
sort(bian+1,bian+m+1,cmp); //快速排序,m表示输入的边的数量
kruskal(); //调用函数“最小生成树的kruskal算法”
return 0;
}