当前位置: 首页 > news >正文

C++ 最小生成树 洛谷

介绍:

最小生成树是个啥?其实就像杨志一行人押送生辰纲。抛开最后生辰纲被抢的结局不谈,杨志他们需要到好几个地方,每个地方都需要花点过路费给梁山好汉们打点。比如下面就是一张城市地图:

地图

其中每两个图之间的路径长就是要给梁山好汉们打点的银子数。比如1号地点到2号地点的梁山好汉需要2两银子。那么问题来了,怎样才能选择其中一部分道路就可以到达所有地点且让总花费值最小呢???

如果有n个节点(城市),那就至少要连(n-1)条边(路线),并且肯定没有回路。(不信?画几个图试试)众所周知,在无向图中,只要这个图里没有回路(有(n-1)条边),那它就无疑是棵树了。(还是不信?画图......)所以,**我们就管这棵各边权值(费用)和叫“最小生成树”**了。

那这个最小生成树到底怎么搞呢?我们伟大的先哲发明了两种算法:一种叫Kruskal算法,另一种叫Prim算法。我们这里就来介绍一下名字字典序靠前的那个Kruskal吧!(我才不会告诉你根本原因是本蒟蒻不会用Prim呢!)

那咱就开始吧!首先,我们读入的数据是这样的!

1 2 2
1 3 2
1 4 4
2 3 3
3 4 4

其中每一行的a,b,c 3个数是指a到b的路径权值是c哦!

既然题目中让我们求“最小”生成树,那咱们就自然而然地想到先把这些边排个序,先用最小的边,从小到大一路把这棵树搞出来

说干就干,排一个呗!

1 2 2
1 3 2 
2 3 3
1 4 4
3 4 4

排好之后,映入我们眼帘的是“1,2,2”这条边,二话不说,把它放到生成树里!

过程1

接下来是“1,2,2”,很好,把它也加进去

然后是“2,3,3”,好多同学把它都加了进去......

等会等会,先别忙着加!好像有什么猫腻!前面说过,最小生成树,是不能带回路的,加进去一不是最小,二它连个树都不是,所以不能加!于是我们意识到:在每次加进去新边之前,一定要看看它和其它边会不会构成回路才行!

那咱就只能退而求其次,选其它的边啦,“1,4,4”这可不是回路,没问题!

至此,边数已达(n-1)条,所有点已经选上,最小生成树大功告成!(当然,选下面的“3,4,4”也是阔以的)

总结一下,我们刚才是怎样搞出这棵最小生成树的:

  1. 把边们从小到大排个序
  2. 如果没有回路,一条一条往里加
  3. 边数达到(n-1,完成

步骤都弄懂啦!但是这个“回路”到底怎么判断呢?

所谓判断回路,就是看看有了这条边之后,有没有连通的部分,从“”的角度来看,就是两个点的根节点(也就是“祖宗”)是不是一样的

哎?话说说到这里,诸位有没有联想到什么啊?

没错,就是金光闪闪的并!查!集!

并查集是啥就不在我的责任范围之内啦,(其实就是懒癌犯了)这里我就默认泥萌都会并查集了......

所以说我们可以搞个并查集出来,然后每加入条边时,先用并查集的“查询”功能,看看两个节点的祖宗是不是一样的,如果不一样就把他俩“合并”,就可以放心大胆的往里加啦~

P3366 【模板】最小生成树

题目描述

如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz

输入格式

第一行包含两个整数 N,M,表示该图共有 N 个结点和 M 条无向边。

接下来 M 行每行包含三个整数 Xi,Yi,Zi​,表示有一条长度为 Zi​ 的无向边连接结点 Xi,Yi​。

输出格式

如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz

#include<bits/stdc++.h>
#define int long long
#define PII pair<int,int >
#define fi first
#define se second
#define pb push_back
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)using namespace std;const int N = 1e6+10;int n,m;
int acc[N];pair<int,pair<int,int>> va[N];int find(int x)
{if(acc[x]!=x) acc[x] = find(acc[x]);return acc[x];
}void krukal()
{int cnt = 0,sum = 0;for(int i = 1;i<=n;i++) acc[i] = i;for(int i = 1;i<=m;i++){int a = va[i].se.fi,b = va[i].se.se,c = va[i].fi;int t1 = find(a),t2 = find(b);if(t1!=t2){acc[t1] = t2;sum += c;cnt++;}}if(cnt==n-1) cout<<sum;else cout<<"orz";
}signed main()
{IOS;cin>>n>>m;for(int i = 1;i<=m;i++){int a,b,c;cin>>a>>b>>c;va[i] = {c,{a,b}};}sort(va+1,va+1+m);krukal();return 0;
}

P2330 [SCOI2005] 繁忙的都市

题目描述

城市 C 是一个非常繁忙的大都市,城市中的道路十分的拥挤,于是市长决定对其中的道路进行改造。城市 C 的道路是这样分布的:城市中有 nn 个交叉路口,有些交叉路口之间有道路相连,两个交叉路口之间最多有一条道路相连接。这些道路是双向的,且把所有的交叉路口直接或间接的连接起来了。每条道路都有一个分值,分值越小表示这个道路越繁忙,越需要进行改造。但是市政府的资金有限,市长希望进行改造的道路越少越好,于是他提出下面的要求:

  1. 改造的那些道路能够把所有的交叉路口直接或间接的连通起来。
  2. 在满足要求 1 的情况下,改造的道路尽量少。
  3. 在满足要求 1、2 的情况下,改造的那些道路中分值最大的道路分值尽量小。

任务:作为市规划局的你,应当作出最佳的决策,选择哪些道路应当被修建。

输入格式

第一行有两个整数 n,m 表示城市有 n 个交叉路口,m 条道路。

接下来 m 行是对每条道路的描述,u,v,c 表示交叉路口 u 和 v 之间有道路相连,分值为 c。

输出格式

两个整数 s,max,表示你选出了几条道路,分值最大的那条道路的分值是多少。

#include<bits/stdc++.h>
#define int long long
#define PII pair<int,int >
#define fi first
#define se second
#define pb push_back
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)using namespace std;const int N = 1e6+10;int n,m;
int acc[N];pair<int,pair<int,int>> va[N];int find(int x)
{if(acc[x]!=x) acc[x] = find(acc[x]);return acc[x];
}void krukal()
{int cnt = 0,sum = 0;for(int i = 1;i<=n;i++) acc[i] = i;for(int i = 1;i<=m;i++){int a = va[i].se.fi,b = va[i].se.se,c = va[i].fi;int t1 = find(a),t2 = find(b);if(t1!=t2){acc[t1] = t2;cnt++;}if(cnt==n-1) {cout<<cnt<<" "<<c;break;}}}signed main()
{IOS;cin>>n>>m;for(int i = 1;i<=m;i++){int a,b,c;cin>>a>>b>>c;va[i] = {c,{a,b}};}sort(va+1,va+1+m);krukal();return 0;
}

P1195 口袋的天空

题目背景

小杉坐在教室里,透过口袋一样的窗户看口袋一样的天空。

有很多云飘在那里,看起来很漂亮,小杉想摘下那样美的几朵云,做成棉花糖。

题目描述

给你云朵的个数 N,再给你 M 个关系,表示哪些云朵可以连在一起。

现在小杉要把所有云朵连成 K 个棉花糖,一个棉花糖最少要用掉一朵云,小杉想知道他怎么连,花费的代价最小。

输入格式

第一行有三个数 N,M,K。

接下来 M 行每行三个数 X,Y,L,表示 X 云和 Y 云可以通过 L 的代价连在一起。

输出格式

对每组数据输出一行,仅有一个整数,表示最小的代价。

如果怎么连都连不出 K 个棉花糖,请输出 No Answer

题解:

首先让我们选k朵云做棉花糖,换句话说就是选k个节点构造最小生成树,那不好办!我们都知道,全部n个点的最小生成树有(n-1)条边,那选k个节点就是(n-k)条边呗!

其次就是这个“No Answer”怎么弄的问题,其实也so easy,你想啊,一共就m条边,如果都选完了还没有选出(n-k)条边来,那就是妥妥的No Answer 了

说了这么多,是时候放代码了!听我这么一讲解,大家应该都理解最小生成树的Kruskal算法了吧!

#include<bits/stdc++.h>
#define int long long
#define PII pair<int,int >
#define fi first
#define se second
#define pb push_back
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)using namespace std;const int N = 1e5+10;int n,m,k;
int acc[N];pair<int,pair<int,int>> va[N];int find(int x)
{if(acc[x]!=x) acc[x] = find(acc[x]);return acc[x];
}void krukal()
{int cnt = 0,sum = 0;for(int i = 1;i<=n;i++) acc[i] = i;for(int i = 1;i<=m;i++){int a = va[i].se.fi,b = va[i].se.se,c = va[i].fi;int t1 = find(a),t2 = find(b);if(t1!=t2){acc[t1] = t2;cnt++;sum += c;}if(cnt==n-k) break;}if(cnt==n-k) cout<<sum;else cout<<"No Answer";
}signed main()
{IOS;cin>>n>>m>>k;for(int i = 1;i<=m;i++){int a,b,c;cin>>a>>b>>c;va[i] = {c,{a,b}};}sort(va+1,va+1+m);krukal();return 0;
}

P1547 [USACO05MAR] Out of Hay S

题目描述

Bessie 计划调查 N(2≤N≤2 000)个农场的干草情况,它从 1 号农场出发。农场之间总共有 M(1≤M≤104)条双向道路,所有道路的总长度不超过 109。有些农场之间存在着多条道路,所有的农场之间都是连通的。

Bessie 希望计算出该图中最小生成树中的最长边的长度。

输入格式

第一行两个整数 N,M。

接下来 M 行,每行三个用空格隔开的整数 Ai,Bi,Li,表示 Ai,Bi 之间有一条道路,长度为 Li​。

输出格式

一个整数,表示最小生成树中的最长边的长度。

#include<bits/stdc++.h>
#define int long long
#define PII pair<int,int >
#define fi first
#define se second
#define pb push_back
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)using namespace std;const int N = 1e6+10;int n,m;
int acc[N];pair<int,pair<int,int>> va[N];int find(int x)
{if(acc[x]!=x) acc[x] = find(acc[x]);return acc[x];
}void krukal()
{int cnt = 0,sum = 0;for(int i = 1;i<=n;i++) acc[i] = i;for(int i = 1;i<=m;i++){int a = va[i].se.fi,b = va[i].se.se,c = va[i].fi;int t1 = find(a),t2 = find(b);if(t1!=t2){acc[t1] = t2;cnt++;sum += c;}if(cnt==n-1) {cout<<c;break;}}
}signed main()
{IOS;cin>>n>>m;for(int i = 1;i<=m;i++){int a,b,c;cin>>a>>b>>c;va[i] = {c,{a,b}};}sort(va+1,va+1+m);krukal();return 0;
}

P2872 [USACO07DEC] Building Roads S

题目描述

给定 n 个点的坐标,第 i 个点的坐标为 (xi,yi),这 n 个点编号为 1 到 n。给定 m 条边,第 i 条边连接第 ui​ 个点和第 vi​ 个点。现在要求你添加一些边,并且能使得任意一点都可以连通其他所有点。求添加的边的总长度的最小值。

输入格式

第一行两个整数 n,m 代表点数与边数。
接下来 n 行每行两个整数 xi​,yi​ 代表第 i 个点的坐标。
接下来 m 行每行两个整数 ui,vi 代表第 i 条边连接第 ui​ 个点和第 vi​ 个点。

输出格式

一行一个实数代表添加的边的最小长度,要求保留两位小数,为了避免误差, 请用 64 位实型变量进行计算。

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define pb push_back
#define fi first
#define int long long
#define pii pair<int,int >
#define se secondusing namespace std;const int N=2e6+10;
int acc[N];
int n,m;struct node
{int u,v;double w;
}va[N];struct Node
{int x,y;
}vb[N];bool cmp(node a,node b)
{if(a.w!=b.w) return a.w<b.w;
}int find(int x)
{if(acc[x]!=x) acc[x]=find(acc[x]);return acc[x];
}signed main()
{IOS;cin>>n>>m;	int sum=0;double sum1 = 0.0;for(int i=1;i<=n;i++){cin>>vb[i].x>>vb[i].y;}for(int i=1;i<=n;i++) acc[i]=i;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){sum++;va[sum].u=i;va[sum].v=j;va[sum].w=sqrt(pow(vb[i].x-vb[j].x,2)+pow(vb[i].y-vb[j].y,2));}}for(int i=1;i<=m;i++){sum++;int a,b;cin>>a>>b;va[sum].u=a;va[sum].v=b;va[sum].w=0.0;}sort(va+1,va+1+sum,cmp);int cnt = 0;for(int i=1;i<=n*n;i++){if(find(va[i].u)!=find(va[i].v)){acc[find(va[i].u)]=find(va[i].v);sum1+=va[i].w;cnt++;}else continue;if(cnt==n-1) printf("%.2lf",sum1);}
}

P2820 局域网

题目背景

某个局域网内有 n 台计算机,由于搭建局域网时工作人员的疏忽,现在局域网内的连接形成了回路,我们知道如果局域网形成回路那么数据将不停的在回路内传输,造成网络卡的现象。因为连接计算机的网线本身不同,所以有一些连线不是很畅通,我们用 f(i,j) 表示 i,j 之间连接的畅通程度,f(i,j) 值越小表示 i,j 之间连接越通畅,f(i,j) 为 0 表示 i,j 之间无网线连接。

题目描述

现在需要解决回路问题,我们将除去一些连线,使得网络中没有回路,不改变原图节点的连通性,并且被除去网线的 ∑f(i,j) 最大,请求出这个最大值。

输入格式

第一行两个正整数 n,k。

接下来的 kk 行每行三个正整数 i,j,m 表示 i,j 两台计算机之间有网线联通,通畅程度为 m。

输出格式

一个正整数, ∑f(i,j) 的最大值。

#include<bits/stdc++.h>
#define int long long
#define PII pair<int,int >
#define fi first
#define se second
#define pb push_back
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)using namespace std;const int N = 1e5+10;int n,k,ans;
int acc[N];pair<int,pair<int,int>> va[N];int find(int x)
{if(acc[x]!=x) acc[x] = find(acc[x]);return acc[x];
}void krukal()
{int cnt = 0,sum = 0;for(int i = 1;i<=n;i++) acc[i] = i;for(int i = 1;i<=k;i++){int a = va[i].se.fi,b = va[i].se.se,c = va[i].fi;int t1 = find(a),t2 = find(b);if(t1!=t2) {acc[t1] = t2;cnt++;sum += c;}if(cnt==n-1) break;}if(cnt==n-1) cout<<ans-sum;	
}signed main()
{IOS;cin>>n>>k;for(int i = 1;i<=k;i++){int a,b,c;cin>>a>>b>>c;va[i] = {c,{a,b}};ans += c;}sort(va+1,va+1+k);krukal();return 0;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 群晖NAS结合内网穿透工具实现远程连接内网SFTP服务传输文件
  • 【人工智能基础二】人工神经网络基础
  • JAVA通过debezium实时采集mysql数据
  • Unity UnityWebRequest封装类
  • Java学习Day20:基础篇10
  • 二进制与进制转换与原码、反码、补码详解--内含许多超详细图片讲解!!!
  • React(四):DOCX文件在线预览
  • 2024杭电多校(5) 1008. 猫咪们狂欢【带权最大独立集】
  • 宅家也能高效办公?试试这四款款远程控制神器!
  • 【2024年华数杯全国大学生数学建模竞赛】C题:老外游中国 问题思路分析及Python代码实现
  • C语言初阶(12)
  • 周鸿祎回应将成三六零第一大股东:会和公司一起走下去
  • 学习硬件测试04:触摸按键+PWM 驱动蜂鸣器+数码管(P62~P67、P71、P72)
  • mysql介绍
  • 1、.Net UI框架:WPF - .Net宣传系列文章
  • 11111111
  • css布局,左右固定中间自适应实现
  • extjs4学习之配置
  • Javascript基础之Array数组API
  • mac修复ab及siege安装
  • Mysql5.6主从复制
  • Node项目之评分系统(二)- 数据库设计
  • Otto开发初探——微服务依赖管理新利器
  • pdf文件如何在线转换为jpg图片
  • vue从入门到进阶:计算属性computed与侦听器watch(三)
  • 案例分享〡三拾众筹持续交付开发流程支撑创新业务
  • 基于HAProxy的高性能缓存服务器nuster
  • 快速体验 Sentinel 集群限流功能,只需简单几步
  • 设计模式(12)迭代器模式(讲解+应用)
  • 数据科学 第 3 章 11 字符串处理
  • 温故知新之javascript面向对象
  • ​LeetCode解法汇总2696. 删除子串后的字符串最小长度
  • # Swust 12th acm 邀请赛# [ K ] 三角形判定 [题解]
  • #Datawhale X 李宏毅苹果书 AI夏令营#3.13.2局部极小值与鞍点批量和动量
  • $(document).ready(function(){}), $().ready(function(){})和$(function(){})三者区别
  • $L^p$ 调和函数恒为零
  • (~_~)
  • (1)Jupyter Notebook 下载及安装
  • (19)夹钳(用于送货)
  • (4) openssl rsa/pkey(查看私钥、从私钥中提取公钥、查看公钥)
  • (4.10~4.16)
  • (NO.00004)iOS实现打砖块游戏(九):游戏中小球与反弹棒的碰撞
  • (备忘)Java Map 遍历
  • (层次遍历)104. 二叉树的最大深度
  • (附源码)计算机毕业设计SSM保险客户管理系统
  • (个人笔记质量不佳)SQL 左连接、右连接、内连接的区别
  • (一)基于IDEA的JAVA基础10
  • (一)项目实践-利用Appdesigner制作目标跟踪仿真软件
  • (原)Matlab的svmtrain和svmclassify
  • (原)记一次CentOS7 磁盘空间大小异常的解决过程
  • (转贴)用VML开发工作流设计器 UCML.NET工作流管理系统
  • .NET CF命令行调试器MDbg入门(三) 进程控制
  • .net php 通信,flash与asp/php/asp.net通信的方法
  • .NET/C# 获取一个正在运行的进程的命令行参数
  • .Net+SQL Server企业应用性能优化笔记4——精确查找瓶颈