最小生成树问题顾名思义,归纳来讲就是路修的最短。node
接下来引入几个一看就明白的定义:ios
最小生成树相关概念:算法
带权图:边赋以权值的图称为网或带权图,带权图的生成树也是带权的,生成树T各边的权值总和称为该树的权。spa
最小生成树(MST):权值最小的生成树。.net
最小生成树的性质:假设G=(V,E)是一个连通网,U是顶点V的一个非空子集。若(u,v)是一条具备最小权值的边,其中u∈U,v∈V-U,则必存在一棵包含边(u,v)的最小生成树。3d
完成构造网的最小生成树必须解决下面两个问题:code
(1)尽量选取权值小的边,但不能构成回路;blog
(2)选取n-1条恰当的边以连通n个顶点;排序
prim算法适合稠密图,kruskal算法适合简单图。get
kruskal远离更为简单粗暴,可是须要借助并查集这一知识。
(本人写的一篇https://blog.csdn.net/qq_41754350/article/details/81271567)
克鲁斯卡尔算法的基本思想是以边为主导地位,始终选择当前可用的最小边权的边(能够直接快排或者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; }