[学习笔记] MST(最小生成树) - 图论
[学习笔记] MST(最小生成树) - 图论
MST,最小生成树,一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。——百度百科
对于最小生成树,有几个比较常见的性质:
- 对于任意最小生成树,它包含所有的n个节点以及n-1条边。
- 若边权都不相等的话,则它是唯一的。(由此可求第k小生成树)
- 最小生成树里不存在环。
对于找到最小生成树,有几个常见的算法:
kruskal
图片来源于oi wiki(下同)
主要运用贪心思想,按边权的大小排序,时间复杂度\(O(m\, logm)\),适合用于稀疏图,通常跑的非常快,代码也较为简单。
//洛谷P3366版子 下同
#include<bits/stdc++.h>
using namespace std;
#define s(n) scanf("%d", &n)
int n, m, f[5010];
struct Edge{ int u, v, w; }e[200100];
bool cmp(Edge a, Edge b){
return a.w < b.w;
}
inline int ff(int k){
if(f[k] == k) return k;
return f[k] = ff(f[k]);
}
inline void kruskal(){
sort(e + 1, e + m + 1, cmp);
for(int i=1; i<=n; i++) f[i] = i;
int ans = 0, cnt = 0;
for(int i=1; i<=m; i++){
if(cnt == n - 1) break; //小小的优化,可要可不要
int fa = ff(e[i].u), fb = ff(e[i].v);
if(fa == fb) continue;
else{
ans += e[i].w;
cnt++;
f[fa] = fb;
}
}
if(cnt == n - 1) printf("%d", ans);
else printf("orz");
return;
}
int main(){
s(n), s(m);
for(int i=1; i<=m; i++) s(e[i].u), s(e[i].v), s(e[i].w);
kruskal();
return 0;
}
prim
prim算法是由Dijkstra发明的,并且与dijkstra最短路算法发表在同一篇论文中。prim算法与dijksrta非常像,复杂度\(O(m\,longn)\)。适合稠密图。但它跑的不一定有kruskal快。
#include<bits/stdc++.h>
using namespace std;
#define s(n) scanf("%d", &n)
int n, m, x, y, z;
struct edge{
int to, w;
edge(int a, int b){
to = a, w = b;
}
};
vector<edge> G[200001];
struct node{
int id, dis;
node(int a, int b){
id = a, dis = b;
}
bool operator < (const node &u) const{
return dis > u.dis;
}
};
bool done[5005];
inline void prim(){
int s = 1;
for(int i=1; i<=5005; i++) done[i] = 0;
priority_queue<node> q;
q.push(node(s, 0));
int ans = 0, cnt = 0;
while(!q.empty()){
node u = q.top(); q.pop();
if(done[u.id]) continue;
done[u.id] = 1;
ans += u.dis;
cnt++;
for(int i=0; i<G[u.id].size(); i++){
edge y = G[u.id][i];
if(done[y.to]) continue;
q.push(node(y.to, y.w));
}
}
if(cnt == n) printf("%d", ans);
else printf("orz");
return;
}
int main(){
s(n), s(m);
for(int i=1; i<=m; i++){
s(x), s(y), s(z);
G[x].push_back(edge(y, z));
G[y].push_back(edge(x, z));
}
prim();
return 0;
}
例题说明
它在实际运用中非常的灵活:
1.灵活选边
题目描述:
Tyvj已经一岁了,网站也由最初的几个用户增加到了上万个用户,随着Tyvj网站的逐步壮大,管理员的数目也越来越多,现在你身为Tyvj管理层的联络员,希望你找到一些通信渠道,使得管理员两两都可以联络(直接或者是间接都可以)。Tyvj是一个公益性的网站,没有过多的利润,所以你要尽可能的使费用少才可以。
目前你已经知道,Tyvj的通信渠道分为两大类,一类是必选通信渠道,无论价格多少,你都需要把所有的都选择上;还有一类是选择性的通信渠道,你可以从中挑选一些作为最终管理员联络的通信渠道。数据保证给出的通行渠道可以让所有的管理员联通。
思路
比如在联络员一题中,你需要现将必须纳入的边直接加到ans里去,然后再排序可加可不加的边,跑算法
#include<bits/stdc++.h>
using namespace std;
#define s(n) scanf("%d", &n)
int n, m, x, y, z, p, f[2010], ans=0, cnt=1;
struct edge{ int u, v, w; }e[10010];
bool cmp(edge a, edge b){ return a.w < b.w; }
inline int ff(int k){
if(f[k] != k) f[k] = ff(f[k]);
return f[k];
}
inline void kruskal(){
sort(e + 1, e + cnt, cmp);
for(int i=1; i<=cnt; i++){
int fa = ff(e[i].u), fb = ff(e[i].v);
if(fa == fb) continue;
f[fa] = fb;
ans += e[i].w;
}
printf("%d", ans);
}
int main(){
s(n), s(m);
for(int i=1; i<2010; i++) f[i] = i;
for(int i=1; i<=m; i++){
s(p), s(x), s(y), s(z);
if( p == 1){
int fa = ff(x), fb = ff(y);
f[fa] = fb;
ans += z;
}
e[cnt++] = (edge){x, y, z};
}
kruskal();
return 0;
}
2.设置虚点
题目描述
农夫约翰决定给他的N(1<=N<=300)个牧场浇水,这些牧场被自然的命名为1..N。他可以给一个牧场引入水通过在这个牧场挖一口井或者修一条管道使这个牧场和一个已经有水的牧场连接。
在牧场i挖一口井的花费是w_i(1<=w_i<=100000)。修建一条水管连接牧场i和牧场j的花费是p_ij(1<=p_ij<=100000;p_ij=p_ji;p_ii=0)。
请确定农夫约翰为了完成浇灌所有的牧场所需的最小的总花费。
思路
在挖水井中,我原先以为找到最小花费的井然后以这个井为初始点跑prim就行。但我错了,因为最小井可能不唯一,而且MST也可能不唯一。所以可以设置一个虚点,把所有井都连到这个点上去,边权为挖井的花费。用这个有n+1的图跑算法就行了。
#include<bits/stdc++.h>
using namespace std;
#define s(n) scanf("%d", &n)
int n, x, minn = INT_MAX, cnt=1, f[310];
long long ans = 0;
struct edge{
int u, v, w;
}e[900001];
bool cmp(edge a, edge b){
return a.w < b.w;
}
inline int ff(int k){
if(f[k] != k) f[k] = ff(f[k]);
return f[k];
}
inline void kruskal(){
sort(e + 1, e + 1 + n*n + n, cmp);
for(int i=1; i<=n; i++) f[i] = i;
int cnt = 0;
for(int i=1; i<=n*n+n; i++){
if(cnt == n) break;
int fa = ff(e[i].u), fb = ff(e[i].v);
if(fa == fb) continue;
f[fa] = fb;
cnt++;
ans += e[i].w;
}
printf("%lld", ans);
return;
}
int main(){
s(n);
for(int i=1; i<=n; i++){
s(x);
e[cnt++] = (edge){i, n+1, x};
e[cnt++] = (edge){n+1, i, x};
}
for(int i=1; i<=n; i++){
for(int j=1; j<=n; j++){
s(x);
if(i == j) continue;
e[cnt++] = (edge){i, j, x};
}
}
kruskal();
return 0;
}
合理调整边权
题目描述
Farmer John变得非常懒, 他不想再继续维护供奶牛之间供通行的道路. 道路被用来连接N 个牧场, 牧场被连续地编号为1..N. 每一个牧场都是一个奶牛的家. FJ计划除去P条
道路中尽可能多的道路, 但是还要保持牧场之间的连通性. 你首先要决定那些道路是需要保留的N-1条道路. 第j条双向道路连接了牧场S_j和E_j , 而且走完它需要L_j 的时间.
没有两个牧场是被一条以上的道路所连接. 奶牛们非常伤心, 因为她们的交通系统被削减了. 你需要到每一个奶牛的住处去安慰她们. 每次你到达第i个牧场的时候(即使你已
经到过), 你必须花去C_i的时间和奶牛交谈. 你每个晚上都会在同一个牧场(这是供你选择的)过夜, 直到奶牛们都从悲伤中缓过神来. 在早上起来和晚上回去睡觉的时候, 你都
需要和在你睡觉的牧场的奶牛交谈一次. 这样你才能完成你的交谈任务. 假设Farmer John采纳了你的建议, 请计算出使所有奶牛都被安慰的最少时间。
思路
安慰奶牛这道题中,根据题干n-1条边可知要求MST。通过画个草图可以知道每个边都要跑两遍,但还要考虑每个点的权值,这怎么办呢?模拟一下,在MST上,我们走每一条边时,都要加上起始点和到达点的权值,并且加边权时并不是加一次,而是要加两次。整个过程中最后一定会回到开始选择的那个点,所以还要再加一次开始点的权值。 于是每条边的边权就变成了这个样子:\(edge\_ fine[i]\, =\, begin\, +\, end\, +\, edge[i]*2\)
然后用新权值跑算法就行了。不过在跑的时候还要找到权值最小的开始点,这很简单,对吧~
#include<bits/stdc++.h>
using namespace std;
#define s(n) scanf("%d", &n)
int n, p, x, y, z, f[10001], cnt, ans=0, node[10001], mn=INT_MAX;
struct edge{ int u, v, w; }e[100010];
bool cmp(edge a, edge b){ return a.w < b.w; }
inline int ff(int k){
if(f[k] != k) f[k] = ff(f[k]);
return f[k];
}
inline void kruskal(){
for(int i=1; i<=n; i++) f[i] = i;
sort(e + 1, e + p + 1, cmp);
cnt = 0;
for(int i=1; i<=p; i++){
if(cnt == n) continue;
int fa = ff(e[i].u), fb = ff(e[i].v);
if(fa == fb) continue;
f[fa] = fb;
cnt++;
ans += e[i].w;
mn = min(mn, min(node[e[i].u], node[e[i].v]));
}
printf("%d", ans + mn);
}
int main(){
s(n), s(p);
for(int i=1; i<=n; i++) s(node[i]);
for(int i=1; i<=p; i++){
s(x), s(y), s(z);
e[i] = (edge){x, y, z * 2 + node[x] + node[y]};
}
kruskal();
return 0;
}
小数据可以打暴力~
the end
[学习笔记] MST(最小生成树) - 图论的更多相关文章
- 【学习笔记】Tarjan 图论算法
- 前言 本文主要介绍 Tarjan 算法的「强连通分量」「割点」「桥」等算法. 争取写的好懂一些. - 「强连通分量」 - 何为「强连通分量」 在有向图中,如果任意两个点都能通过直接或间接的路径相互 ...
- Day 4 学习笔记 各种图论
Day 4 学习笔记 各种图论 图是什么???? 不是我上传的图床上的那些垃圾解释... 一.图: 1.定义 由顶点和边组成的集合叫做图. 2.分类: 边如果是有向边,就是有向图:否则,就是无向图. ...
- 图论学习笔记·$Floyd$ $Warshall$
对于图论--虽然本蒟蒻也才入门--于是有了这篇学习笔记\(qwq\) 一般我们对于最短路的处理,本蒟蒻之前都是通过构建二维数组的方式然后对每两个点进行1次深度或者广度优先搜索,即一共进行\(n\)^2 ...
- 图论 竞赛图(tournament)学习笔记
竞赛图(tournament)学习笔记 现在只是知道几个简单的性质... 竞赛图也叫有向完全图. 其实就是无向完全图的边有了方向. 有一个很有趣的性质就是:一个tournament要么没有环,如果 ...
- OI知识点|NOIP考点|省选考点|教程与学习笔记合集
点亮技能树行动-- 本篇blog按照分类将网上写的OI知识点归纳了一下,然后会附上蒟蒻我的学习笔记或者是我认为写的不错的专题博客qwqwqwq(好吧,其实已经咕咕咕了...) 基础算法 贪心 枚举 分 ...
- BZOJ 2038: [2009国家集训队]小Z的袜子(hose)【莫队算法裸题&&学习笔记】
2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec Memory Limit: 259 MBSubmit: 9894 Solved: 4561[Subm ...
- [转帖][Bash Shell] Shell学习笔记
[Bash Shell] Shell学习笔记 http://www.cnblogs.com/maybe2030/p/5022595.html 阅读目录 编译型语言 解释型语言 5.1 作为可执行程序 ...
- 莫队学习笔记(未完成QAQ
似乎之前讲评vjudge上的这题的时候提到过?但是并没有落实(...我发现我还有好多好多没落实?vjudge上的题目还没搞,然后之前考试的题目也都还没总结?天哪我哭了QAQ 然后这三道题我都是通过一道 ...
- python 学习笔记 13 -- 经常使用的时间模块之time
Python 没有包括相应日期和时间的内置类型.只是提供了3个相应的模块,能够採用多种表示管理日期和时间值: * time 模块由底层C库提供与时间相关的函数.它包括一些函数用于获取时钟时间和处 ...
- 【学习笔记】Kruskal 重构树
1. 例题引入:BZOJ3551 用一道例题引入:BZOJ3551 题目大意:有 \(N\) 座山峰,每座山峰有他的高度 \(h_i\).有些山峰之间有双向道路相连,共 \(M\) 条路径,每条路径有 ...
随机推荐
- http请求方式-HttpClient 微信退款的接口,需要证书请求 https请求
http请求方式-HttpClient import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import ...
- IDEA生成类和方法注释模板详细说明 绝对好用
吐槽 今天心血来潮,将使用了很久的IDEA旗舰版卸载了,想换社区版用一段时间,毕竟社区版开源免费.精简不卡顿,如果够用的话以后就省去了破解的烦恼,而且可以紧跟官网使用最新版 旧的IDEA配置忘记保存了 ...
- 在C#中使用RabbitMQ做个简单的发送邮件小项目
在C#中使用RabbitMQ做个简单的发送邮件小项目 前言 好久没有做项目了,这次做一个发送邮件的小项目.发邮件是一个比较耗时的操作,之前在我的个人博客里面回复评论和友链申请是会通过发送邮件来通知对方 ...
- 【资料分享】全志科技T507工业核心板硬件说明书(下)
目 录 3 电气特性 3.1 工作环境 3.2 功耗测试 3.3 热成像图 4 机械尺寸 5 底板设计注意事项 5.1 最小系统设计 5.1.1 电源设计说明 5.1.2 系统启动配置 5.1. ...
- Web 安全:OWASP TOP10 漏洞介绍
OWASP TOP 10漏洞是指由Open Web Application Security Project(OWASP)发布的十大最严重. 最普遍的Web应用程序安全漏洞.这些漏洞在当今的Web应用 ...
- 把nodejs程序打包成可执行文件
在写好之后的nodejs程序,想发给同事的电脑上运行程序,就不得不下载node环境,还要安装第三方依赖包,非常的麻烦. 因此,可以借助一些插件来完成nodejs程序的打包,变成可以执行的文件. 将No ...
- [oeasy]python0133_[趣味拓展]好玩的unicode字符_另类字符_上下颠倒英文字符
另类字符 回忆上次内容 上次再次输出了大红心<span style="font-size:64px;color:red"></span> 找到了红心对应的编 ...
- 【译】使 Visual Studio 更加可视化
任何 Web.桌面或移动开发人员都经常使用图像.你可以从 C#.HTML.XAML.CSS.C++.VB.TypeScript 甚至代码注释中引用它们.有些图像是本地的,有些存在于线上或网络共享中,而 ...
- Mac下浏览器跨域配置方法
open -n /Applications/Google\ Chrome.app/ --args --disable-web-security --user-data-dir=/Users/hooli ...
- [rCore学习笔记 011]第1章作业题
编程题 第一题 在homework文件夹下创建homework-1-1,使用cargo创建工程: cargo new getFileName 在src下创建file_name.rs文件: // /ho ...