2023-03-08 21:20阅读: 46评论: 0推荐: 0

Kruskal 最小生成树

-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.经典大练习卷题

P2872 Building Roads S

P1546 最短网络

P2330 繁忙的都市

P2820 局域网

P1195 口袋的天空

P1396 营救

P1194 买礼物

P2121 拆地毯

P4047 部落划分

-6.总结

因为你学好了Kruskal最小生成树、Kruskal最小生成树、Kruskal最小生成树等高级算法,所以你才能学好Kruskal最小生成树,学好了Kruskal最小生成树,你就能学好Kruskal最小生成树,你学好了Kruskal最小生成树,所以恭喜你,你学好了Kruskal最小生成树!!!

本文作者:Luckies

本文链接:https://www.cnblogs.com/Luckies/p/kruskal.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Luckies  阅读(46)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起