前言

视频链接

视频连接:https://www.bilibili.com/video/BV1wV411s7Pe

练习题单

SWPUOJ题单:http://acm.mangata.ltd/training/61d5783bc488fd0507f8214a

题目连接 题目名
https://acm.dingbacode.com/showproblem.php?pid=1863 畅通工程
https://acm.dingbacode.com/showproblem.php?pid=1875 畅通工程再续
https://acm.dingbacode.com/showproblem.php?pid=1879 继续畅通工程
https://www.luogu.com.cn/problem/P3366 P3366 【模板】最小生成树
https://www.luogu.com.cn/problem/P2872 P2872 [USACO07DEC]Building Roads S
https://www.luogu.com.cn/problem/P1195 P1195 口袋的天空
https://www.luogu.com.cn/problem/P1194 P1194 买礼物
https://www.luogu.com.cn/problem/P2121 P2121 拆地毯
https://www.luogu.com.cn/problem/P1396 P1396 营救
https://www.luogu.com.cn/problem/P1991 P1991 无线通讯网
https://www.luogu.com.cn/problem/P4047 P4047 [JSOI2010]部落划分
https://acm.dingbacode.com/showproblem.php?pid=1598 find the most comfortable road

什么是最小生成树

在讲最小生成树之前,我们先回顾一下什么是生成树:对于无向图G和一棵树T来说,如果T是G的子图,则称T为G的树,如果T是G的生成子图,则称T是G的生成树。而最小生成树就是对于一个有权值的图来说最小权值和的图就是最小生成树(也就是边权和最小的连通图,且只有n-1条边,其实也就是一颗树)

Kruskal算法

该算法的基本思想是从小到大加入边,是个贪心算法。Kruskal 算法是一种常见并且好写的最小生成树算法,由 Kruskal 发明。

原理虽然简单,但是需要一种数据结构维护一个森林,不能使其成环,或者说是维护多个集合,然后每次合并两个元素或者集合,这很容易和昨天学习的并查集联系起来,我们可以用并查集很轻松的维护这个森林。

如果使用 O(mlog2m)O(mlog⁡2m)O(mlog_2m)O(mlog⁡_2m)O(mlog2​m)O(mlog⁡2​m) 的排序算法,并且使用 O(mα(m,n))O(mα(m,n))O(mα(m,n))O(mα(m,n))O(mα(m,n))O(mα(m,n))或 O(mlog2n)O(mlog2⁡n)O(mlog_2n)O(mlog_2⁡n)O(mlog2​n)O(mlog2​⁡n) 的并查集,就可以得到时间复杂度为O(mlog2m)O(mlog⁡2m)O(mlog_2m)O(mlog⁡_2m)O(mlog2​m)O(mlog⁡2​m)的 Kruskal 算法。

证明

为了造出一棵最小生成树,我们从最小边权的边开始,按边权从小到大依次加入,如果某次加边产生了环,就扔掉这条边,直到加入了n−1n−1n−1条边,即形成了一棵树。

证明:使用归纳法,证明任何时候 K 算法选择的边集都被某棵 MST 所包含。

基础:对于算法刚开始时,显然成立(最小生成树存在)。

归纳:假设某时刻成立,当前边集为 F,令 T 为这棵 MST,考虑下一条加入的边 e。

如果 eee 属于 TTT,那么成立。

否则,T+eT+eT+e一定存在一个环,考虑这个环上不属于 FFF 的另一条边 fff(一定只有一条)。

首先,fff的权值一定不会比eee小,不然fff会在 eee之前被选取。

然后,fff的权值一定不会比 e 大,不然T+e−fT+e−fT+e−f就是一棵比TTT还优的生成树了。

所以,T+e−fT+e−fT+e−f包含了FFF,并且也是一棵最小生成树,归纳成立。

代码实现

(以hdu1863为例)

#include<bits/stdc++.h>
using namespace std;const int N = 1e2+10;
int fa[N];
int m,n;
struct edge {int u,v,w;bool operator < (const edge & a) const {return this->w < a.w;}
};
vector<edge> V;void init() {for(int i = 1;i <= m; ++i) {fa[i] = i;}V.clear();
}int find(int x) {while(x != fa[x]) x= fa[x];return x;
}void kruskal() {int ans = 0;int cnt = m;for(int i = 0;i < V.size(); ++i) {int u = V[i].u;int v = V[i].v;int w = V[i].w;u = find(u);v = find(v);if(u != v) {fa[v] = u;ans += w;cnt--;}}if(cnt == 1) printf("%d\n",ans);else printf("?\n");
}int main()
{while(~scanf("%d%d",&n,&m) && n) {init();for(int i = 1;i <= n; ++i) {int u,v,w;scanf("%d%d%d",&u,&v,&w);V.push_back({u,v,w});}sort(V.begin(),V.end());kruskal();}return 0;
}

Prim 算法

Prim 算法是另一种常见并且好写的最小生成树算法。该算法的基本思想是从一个结点开始,不断加点(而不是 Kruskal 算法的加边)。具体来说,每次要选择距离最小的一个结点,以及用新的边更新其他结点的距离。

其实跟 Dijkstra 算法一样,每次找到距离最小的一个点,可以暴力找也可以用堆维护。

堆优化的方式类似 Dijkstra 的堆优化,但如果使用二叉堆等不支持 O(1) decrease-key 的堆,复杂度就不优于 Kruskal,常数也比 Kruskal 大。所以,一般情况下都使用 Kruskal 算法,在稠密图尤其是完全图上,暴力 Prim 的复杂度比 Kruskal 优,但 不一定 实际跑得更快。所以一般不用prim算法

关于各种优化时间复杂度

暴力:O(n2+m)O(n^2+m)O(n2+m)。

二叉堆:O((n+m)log2n)O((n+m)log_2n)O((n+m)log2​n)。

Fib 堆:O(nlog⁡2n+m)O(nlog⁡_2n+m)O(nlog⁡2​n+m)。

证明

从任意一个结点开始,将结点分成两类:已加入的,未加入的。

每次从未加入的结点中,找一个与已加入的结点之间边权最小值最小的结点。

然后将这个结点加入,并连上那条边权最小的边。

重复 n−1 次即可。

证明:还是说明在每一步,都存在一棵最小生成树包含已选边集。

基础:只有一个结点的时候,显然成立。

归纳:如果某一步成立,当前边集为 F,属于 T 这棵 MST,接下来要加入边 e。

如果 e 属于 T,那么成立。

否则考虑 T+e 中环上另一条可以加入当前边集的边 f。

首先,f 的权值一定不小于 e 的权值,否则就会选择 f 而不是 e 了。

然后,f 的权值一定不大于 e 的权值,否则 T+e−f 就是一棵更小的生成树了。

因此,e 和 f 的权值相等,T+e−f 也是一棵最小生成树,且包含了 F。

代码实现

(以hdu1863为例)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define P pair<int, int>
#define INF 0x3f3f3f3fconst int N = 1005;
int mp[N][N];
bool vis[N];
int dis[N];
int n,m;
int prim(int s) {for(int i = 1; i <=n ; ++i) {dis[i] = INF;vis[i] = false;}dis[s] = 0;int ans = 0;while(true) {int v = -1;for(int u = 1; u <= n; ++u) {if(!vis[u] && (v == -1 || dis[u] < dis[v])) {v = u;}}if(v == -1)break;vis[v] = true;ans += dis[v];for(int u = 1; u <= n; ++u) {dis[u] = min(dis[u], mp[u][v]);}}return ans;
}int main()
{int u,v,cost;while(~scanf("%d%d",&m, &n) && m) {memset(mp,INF,sizeof mp);for(int i = 0; i < m; ++i) {scanf("%d%d%d",&u, &v, &cost);if(mp[u][v] > cost)mp[u][v] = mp[v][u] = cost;}int ans = prim(1);bool is = true;if(ans < INF) {printf("%d\n",ans);} else {puts("?");}}return 0;
}

2021年SWPUACM暑假集训day3最小生成树算法相关推荐

  1. 2021年SWPUACM暑假集训day1二分算法

    二分算法是什么 二分搜索是一种时间复杂为log2nlog_2nlog2​n的算法,可以用于单调函数求根和单调序列查询的有效算法,即使在数列长度在很大的情况下也能很快对其查询,在此同时二分算法也是一种思 ...

  2. 2021年SWPUACM暑假集训day4KMP算法

    什么是KMP算法 KMP算法(也叫看猫片算法(bushi)是Knuth.Pratt 和 Morris 在 1977 年共同发布一个在线性时间(O(n+m))字符串查找或匹配算法,常用于在一个文本串 S ...

  3. 2021年SWPUACM暑假集训day2并查集算法

    什么是并查集 并查集是一种树形的数据结构,顾名思义,它用于处理一些不交集的 合并 及 查询 问题. 它支持两种操作: 1.查找(find):确定某个元素处于哪个子集 2.合并(merge):将两个子集 ...

  4. 2021年SWPUACM暑假集训day5单调栈算法

    什么是单调栈 顾名思义,单调栈即满足单调性的栈结构.这里的单调递增或递减是指的从栈顶到栈底单调递增或递减.既然是栈,就满足后进先出的特点.与之相对应的是单调队列. 单调栈的实现 插入 在当前元素插入栈 ...

  5. 【Nowcoder】2021牛客暑假集训营(第七场): xay loves trees 双指针 + 线段树 + 尺取

    传送门 题意 给你两个树,求一个最大集合,要求集合内的任意两个点在第一个树上,比如是祖先关系,在第二棵树,不能存在祖先关系 分析 某人吐槽我的题解写的太简单了,然后我觉得...承认错误死不悔改 这道题 ...

  6. 【2021软件创新实验室暑假集训】SpringMVC框架(设计原理、简单使用、源码探究)

    系列文章目录 20级 Java篇 [2021软件创新实验室暑假集训]计算机的起源与大致原理 [2021软件创新实验室暑假集训]Java基础(一) [2021软件创新实验室暑假集训]Java基础(二) ...

  7. 【软件创新实验室2021年暑假集训】Java技术培训——Java前置知识学习

    [软件创新实验室2021年暑假集训]Java技术培训--Java前置知识学习 文章目录 [软件创新实验室2021年暑假集训]Java技术培训--Java前置知识学习 前言 一.了解计算机 1.计算机的 ...

  8. 2021暑假集训总结

    ACM暑期集训就要结束了,有许多感想许多收获,一个多快两个月的训练学到了很多.总体来说,这个暑假对我是一个很有收获的暑假,也是第一次在校集训,效率比在家高很多,也写了自己的板子.不仅仅是算法的学习,在 ...

  9. 2016暑假集训总结

    Preface 这是蒟蒻lyd729在初二升初三的暑假里训练的总结. 来看看一年前的lyd729写的暑假集训总结(链接),真是觉得自己长大了好多. 这一年发生了太多故事.(我来讲故事啦) 去年,一升初 ...

最新文章

  1. java notify 的作用_java 为什么notify和notifyAll都不起作用?
  2. R语言生成仿真的3D高斯簇数据集、使用scale函数进行数据缩放、并使用KMeans进行聚类分析、数据反向缩放并比较聚类生成的中心和实际数据的中心的差异、预测新的数据所属的聚类簇
  3. Linux平台Qt creator报错:Circular all - first dependency dropped
  4. 丑憨批的Transformer笔记
  5. python数据分析-数据准备
  6. memcached学习笔记6--浅谈memcached的机制 以及 memcached细节讨论
  7. Spark Java API:foreach、foreachPartition、lookup
  8. vista任务栏透明_增加Windows Vista任务栏预览大小的赏金(付费!)
  9. 操作系统学习笔记-01-操作系统的概念(定义),功能和目标
  10. 你见过在地铁里哭的人吗?扎心了老铁!!
  11. 台积电预计汽车芯片短缺在下一季度将有明显改善 他们已协调产能支持
  12. 《计算机系统:核心概念及软硬件实现(原书第4版)》——1.4数据库系统
  13. 解扩matlab,(完整版)BPSK调制的MATLAB仿真
  14. JavaScript截取文件扩展名
  15. 腾讯笔试题是c语言吗,2019腾讯校园招聘C语言笔试题和面试题答案
  16. 游鸿明歌曲白色恋人浅析
  17. 2013年总结-托福备考,减肥成功,微软实习,VMware工作,硕士毕业
  18. 解决多元线性回归的多重共线性问题
  19. Python实现GWO智能灰狼优化算法优化支持向量机回归模型(svr算法)项目实战
  20. Failed to configure a DataSource

热门文章

  1. VS2019配置opencv4.1.2(永久配置)
  2. ubuntu16.04装机7:安装VScode
  3. 批标准化(batch normalization)与层标准化(layer normalization)比较
  4. 三级联动(ajax)
  5. html5+php实现文件拖动上传功能
  6. auto_ptr scoped_ptr shared_ptr weak_ptr unique_ptr
  7. 一个前端er使用浏览器打印功能快速制作PDF简历
  8. Qt工程pro文件的简单配置(尤其是第三方头文件和库)
  9. 一个简单的实现了智能虚拟女友—图灵机器人
  10. HttpServletResponse中sendError与setStatus的区别