Kruskal
Posted wozuishuaiwozuiniu6
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kruskal相关的知识,希望对你有一定的参考价值。
一、概述
最小生成树问题顾名思义,概括来说就是路修的最短。
最小生成树相关概念:
带权图:边赋以权值的图称为网或带权图,带权图的生成树也是带权的,生成树T各边的权值总和称为该树的权。
最小生成树(MST):权值最小的生成树。
最小生成树的性质:假设G=(V,E)是一个连通网,U是顶点V的一个非空子集。若(u,v)是一条具有最小权值的边,其中u∈U,v∈V-U,则必存在一棵包含边(u,v)的最小生成树。
完成构造网的最小生成树必须解决下面两个问题:
(1)尽可能选取权值小的边,但不能构成回路;
(2)选取n-1条恰当的边以连通n个顶点;
prim算法适合稠密图,kruskal算法适合简单图。
二、kruskal算法
kruskal远离更为简单粗暴,但是需要借助并查集这一知识。
克鲁斯卡尔算法的基本思想是以边为主导地位,始终选择当前可用的最小边权的边(可以直接快排或者algorithm的sort)。每次选择边权最小的边链接两个端点是kruskal的规则,并实时判断两个点之间有没有间接联通。
现在我来模拟一下:
假如有以下几个城市,之间都有相连的道路:
根据kruskal的原理,我们需要对边权dis进行排序,每次找出最小的边。
排序后,最小的边自然是第8条边,于是4和6相连。
遍历继续,第二小的边是1号,1和2联通。
再后来是边3连接1,4。
dis也是14的还有边5,它连接3,4。
其次是dis为15的边4,但是2和4已经相连了,pass。
然后是dis为16的两条边(边2和边9),边2连接1和3,边9连接3和6,它们都已经间接相连,pass。
再然后就是dis为22的边10,它连接5和6,5还没有加入组织,所以使用这边。继续,发现此时已经连接了n-1条边,结束,最后图示如下:
原理如此简单,代码也很好实现(给个模板), 代码如下所示(注意细节):
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,tot=0,k=0;//n端点总数,m边数,tot记录最终答案,k已经连接了多少边
int fat[200010];//记录集体老大
struct node
{
int from,to,dis;//结构体储存边
}edge[200010];
bool cmp(const node &a,const node &b)//sort排序(当然你也可以快排)
{
return a.dis<b.dis;
}
int father(int x)//找集体老大,并查集的一部分
{
if(fat[x]!=x)
return father(fat[x]);
else return x;
}
void unionn(int x,int y)//加入团体,并查集的一部分
{
fat[father(y)]=father(x);
}
int main()
{
scanf("%d%d",&n,&m);//输入点数,边数
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&edge[i].from,&edge[i].to,&edge[i].dis);//输入边的信息
}
for(int i=1;i<=n;i++) fat[i]=i;//自己最开始就是自己的老大 (初始化)
sort(edge+1,edge+1+m,cmp);//按权值排序(kruskal的体现)
for(int i=1;i<=m;i++)//从小到大遍历
{
if(k==n-1) break;//n个点需要n-1条边连接
if(father(edge[i].from)!=father(edge[i].to))//假如不在一个团体
{
unionn(edge[i].from,edge[i].to);//加入
tot+=edge[i].dis;//记录边权
k++;//已连接边数+1
}
}
printf("%d",tot);
return 0;
}
自己的代码(繁忙的都市)
#include<bits/stdc++.h>
using namespace std;
struct node{
int a,b,w;
inline bool operator < (const node &W)const{
return w<W.w;
}
}edge[10010];
int father[10010];
int findfather(int x){
if(x==father[x]){
return x;
}
return father[x]=findfather(father[x]);
}
void unionn(int x,int y){
int fx=findfather(x),fy=findfather(y);
if(fx!=fy){
father[fy]=fx;
}
}
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
father[i]=i;
}
for(int i=1;i<=m;i++){
cin>>edge[i].a>>edge[i].b>>edge[i].w;
}
int vis=0;
sort(edge+1,edge+m+1);
for(int i=1;i<=m;i++){
int fa=findfather(edge[i].a),fb=findfather(edge[i].b);
if(fa!=fb){
unionn(fa,fb);
vis=edge[i].w;
}
}
cout<<n-1<<" "<<vis<<endl;
return 0;
}
以上是关于Kruskal的主要内容,如果未能解决你的问题,请参考以下文章
最小生成树matlab代码Kruskal算法,用于二维网络生成