-1919810.前言
如果你想要学好Kruskal最小生成树的话,那么你就得先学好Kruskal最小生成树,你一看这个Kruskal最小生成树,你想掌握它还真不容易,基础知识里头你就得掌握Kruskal最小生成树、Kruskal最小生成树、甚至还有Kruskal最小生成树等高级算法!!所以这个Kruskal最小生成树的学习是层层递进的。
-114514.前置芝士
1.贪心
2.并查集
3.然后就没有啦
-1.正儿八紧的算法讲解
首先,既然它叫 Kruskal 最小生成树,那么它肯定是 Kruskal 发明的。
其次,最小生成树是什么你都不知道吧,那么就请看下面:
最小生成树
一个无向图的最小生成树就是在这张图中选出它的节点数 \(n\) - \(1\) 条边,使其能够通过这 \(n\) - \(1\) 条边构成一个连通图并且权值最小。
我们知道,一个无向图,如果只有它的节点数 \(n\) - \(1\) 条边,且连通,那么它就是一棵树,并且不能形成环。
所以它的名字叫做最小生成树。
注意,一个不连通的无向图没有最小生成树!
这个算法的核心思想就是贪心。
它的实现是这样的:
首先,我们随便拿出一个无向图:
既然要它的权值和最小,那么我们肯定先选权值最小的一条边
然后再找第二小的边
第三
第四
第五
第六
到了第七小的边时,你会发现它的两个端点已经是连通的了,再选就成一个环了,于是我们跳过它
第八
这时,我们选的边数已经达到了 \(n\) - \(1\) 条边,且图内所有的点都连通了。
因为我们每次选的边都是最小的,所以权值和也肯定是最小的,那么这张图中被标红的边就是这张图的最小生成树。
于是,代码策略就出来了:
1.定义一个结构体存储每一条边的两个端点和权值。
2.将每一条边按照权值从小到大排序。
3.枚举边,用并查集来维护连通性,如果这条边的两个端点在一个集合中,那么跳过它,反之,则将它们连起来,并且把权值和 \(ans\) 加上这条边的权值,选过的边 \(cnt\) + \(1\)(注意,这里的并查集是不用管它原来所有的边的,只管你选了的边,所以它们一开始都不连通)。
4.枚举的过程中,如果你已经选了 \(n\) - \(1\) 条边,那么停止循环。
5.如果最后选过的边不足 \(n\) - \(1\) 条,那么这个图不连通,没有最小生成树,反之则有,它的权值和就是统计过的 \(ans\)。
6.耶!我们做完了!!!
至于它的证明吗,在上面,自己找
-2.这个算法有什么用啊啊啊
这个东东可以用来解决如何用最小的“代价”用 \(n\) - \(1\) 条边连接 \(n\) 个点的问题。
-3.板子题屑讲
震惊全世界的板子题 P3366 【模板】最小生成树(《真》板子,都给你标了)
题目描述
如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz
。
分析
这有什么好分析的!就是个硬得不能再硬的板子!!!
代码(SSSSSSSVIP版含注释不纯享代码)
#include<bits/stdc++.h>
using namespace std;
const int M = 2e5 + 5, N = 5e3 + 5;
struct node//存储每一条边的结构体
{
int x, y, w;
}a[M];
int n, m, fa[N], ans, cnt;//n个结点,m条无向边,fa[i]表示第i个节点所在的集合,ans为权值和,cnt为选的边数
int find(int x)//路径压缩
{
if (fa[x] == x)
return x;
return fa[x] = find(fa[x]);
}
void merge(int x, int y)//合并
{
int fx = find(x), fy = find(y);
if (fx == fy)
return;
fa[fy] = fx;
return;
}
void kruskal()//Kruskal
{
for (int i = 1; i <= n; i++)//并查集初始化
fa[i] = i;
for (int i = 1; i <= m; i++)
{
int fx = find(a[i].x), fy = find(a[i].y);//找到边i的两个端点x,y所在的集合(连通块)
if (fx != fy)//如果不在同一个集合(也就是不连通)
{
merge(fx, fy);//合并
cnt++, ans += a[i].w;//边数+1,权值和+边权
}
if (cnt == n - 1)//如果已经选了n-1条边了,结束循环
return;
}
return;
}
bool cmp(node x, node y)
{
return x.w < y.w;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
cin >> a[i].x >> a[i].y >> a[i].w;
sort(a + 1, a + 1 + m, cmp);//排序
kruskal();//开始Kruskal
if (cnt != n - 1)//如果不连通,输出"orz"
{
cout << "orz";
return 0;
}
cout << ans;//输出答案
return 0;
}
-4.早就该讲的时间复杂度
排序最慢,\(O(mlogm)\),所以总时间复杂度 \(O(mlogm)\),大大滴快!!!
-5.经典大练习卷题
-6.总结
因为你学好了Kruskal最小生成树、Kruskal最小生成树、Kruskal最小生成树等高级算法,所以你才能学好Kruskal最小生成树,学好了Kruskal最小生成树,你就能学好Kruskal最小生成树,你学好了Kruskal最小生成树,所以恭喜你,你学好了Kruskal最小生成树!!!
标签:连通,int,Kruskal,最小,生成,从零开始,权值 From: https://www.cnblogs.com/UchihaTsuki/p/Kruskal-Minimum-Spanning-Tree.html