图论之最小生成树问题(kruskal)
最近有几位同学催我更新,于是来摸摸鱼,来讲一下最小生成树问题。
所谓最小生成树(MST),就是在一张无向带权图中的一棵经过所有节点,边权和最小的一棵树。在实际生活中,可以运用于城镇之间的修路上。
对于MST问题,通常有两种算法,prim算法以及kruskal算法,其中最常用的算法为kruskal算法,其与并查集结合之后可以以O(ElogE)的时间复杂度解决问题,对于稀疏图而言,是一种非常优秀的算法,但是,对于稠密图而言,更适合使用prim算法。这次就只介绍一下kruskal算法。
kruskal算法本质是一种贪心算法,我们知道,树一定无环,我们先对所有边以边权从小到大进行排序,然后从小到大逐个选边,将端点存入并查集中,若边的两个端点发现在同一个并查集中,则会成环,就跳过这个边,否则就把这条边存入MST中。这样我们就得到了一棵最小生成树。易证这种算法的正确性。至于并查集的介绍可以参见我之前的博客。
例题1 hdu1233
还是畅通工程
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 83393 Accepted Submission(s): 37809
当N为0时,输入结束,该用例不被处理。
#include<map>
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
const int maxn=(1e5)+5;
const int maxm=(1e5)+5;
vector<int>g[maxn];
int par[maxn];
int depth[maxn];
int n,m;
//并查集
void init(int n){
for(int i=0;i<=n;i++){
par[i]=i;
depth[i]=0;
}
}
int find(int x){
if(par[x]==x)return x;
else return par[x]=find(par[x]);
}
void unit(int x,int y){
x=find(x);
y=find(y);
if(x==y)return;
if(depth[x]<depth[y])par[x]=y;
else{
par[y]=x;
if(depth[x]==depth[y])depth[x]++;
}
}
bool same(int x,int y){
return find(x)==find(y);
}
//kruskal算法
struct Edge{
int u,v,w;
}edge[maxm];
bool cmp(Edge x,Edge y){
return x.w<y.w;
}
int kruskal(){
int ans=0;
init(n);
sort(edge+1,edge+m+1,cmp);
for(int i=1;i<=m;i++){
int a=find(edge[i].u);
int b=find(edge[i].v);
if(a==b)continue;//成环
unit(a,b);
ans+=edge[i].w;
}
return ans;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
while(n){
m=n*(n-1)/2;
for(int i=1;i<=m;i++){
cin>>edge[i].u>>edge[i].v>>edge[i].w;
}
cout<<kruskal()<<endl;
cin>>n;
}
return 0;
}
例题2 poj2421
| Time Limit: 2000MS | Memory Limit: 65536K | |
| Total Submissions: 35679 | Accepted: 16044 |
Description
We know that there are already some roads between some villages and your job is the build some roads such that all the villages are connect and the length of all the roads built is minimum.
Input
Then there is an integer Q (0 <= Q <= N * (N + 1) / 2). Then come Q lines, each line contains two integers a and b (1 <= a < b <= N), which means the road between village a and village b has been built.
Output
Sample Input
3
0 990 692
990 0 179
692 179 0
1
1 2
Sample Output
179
题目大意:已知有某些边已经建立了,问使这张图联通的代价是多少。
解析:依然是最小生成树问题,只是先预存边到并查集中,再跑kruskal即可。
代码如下
#include<map>
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
const int maxn=105;
const int maxm=10005;
int par[maxn];
int depth[maxn];
int n,m;
void init(int n){
for(int i=0;i<=n;i++){
par[i]=i;
depth[i]=0;
}
}
int find(int x){
if(par[x]==x)return x;
else return par[x]=find(par[x]);
}
void unit(int x,int y){
x=find(x);
y=find(y);
if(x==y)return;
if(depth[x]<depth[y])par[x]=y;
else{
par[y]=x;
if(depth[x]==depth[y])depth[x]++;
}
}
bool same(int x,int y){
return find(x)==find(y);
}
struct Edge{
int u,v,w;
}edge[maxm];
bool cmp(Edge x,Edge y){
return x.w<y.w;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
int cnt=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int x;
cin>>x;
edge[++cnt].u=i;
edge[cnt].v=j;
edge[cnt].w=x;
}
}
m=cnt;
init(n);
int q;
cin>>q;
while(q--){
int x,y;
cin>>x>>y;
if(!same(x,y))unit(x,y);
}
sort(edge+1,edge+m+1,cmp);
int ans=0;
for(int i=1;i<=m;i++){
int a=find(edge[i].u);
int b=find(edge[i].v);
if(a==b)continue;
unit(a,b);
ans+=edge[i].w;
}
cout<<ans<<endl;
return 0;
}
例题3:poj2377
| Time Limit: 1000MS | Memory Limit: 65536K | |
| Total Submissions: 21954 | Accepted: 8892 |
Description
Realizing Farmer John will not pay her, Bessie decides to do the worst job possible. She must decide on a set of connections to install so that (i) the total cost of these connections is as large as possible, (ii) all the barns are connected together (so that it is possible to reach any barn from any other barn via a path of installed connections), and (iii) so that there are no cycles among the connections (which Farmer John would easily be able to detect). Conditions (ii) and (iii) ensure that the final set of connections will look like a "tree".
Input
* Lines 2..M+1: Each line contains three space-separated integers A, B, and C that describe a connection route between barns A and B of cost C.
Output
Sample Input
5 8
1 2 3
1 3 7
2 3 10
2 4 4
2 5 8
3 4 6
3 5 2
4 5 17
Sample Output
42
Hint
The most expensive tree has cost 17 + 8 + 10 + 7 = 42. It uses the following connections: 4 to 5, 2 to 5, 2 to 3, and 1 to 3.
#include<map>
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
const int maxn=(1e3)+5;
const int maxm=(2e4)+5;
vector<int>g[maxn];
int n,m;
int par[maxn];
int depth[maxn];
void init(int n){
for(int i=0;i<=n;i++){
par[i]=i;
depth[i]=0;
}
}
int find(int x){
if(par[x]==x)return x;
else return par[x]=find(par[x]);
}
void unit(int x,int y){
x=find(x);
y=find(y);
if(x==y)return;
if(depth[x]<depth[y])par[x]=y;
else{
par[y]=x;
if(depth[x]==depth[y])depth[x]++;
}
}
bool same(int x,int y){
return find(x)==find(y);
}
struct Edge{
int u,v,w;
}edge[maxm];
bool cmp(Edge x,Edge y){
return x.w>y.w;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>edge[i].u>>edge[i].v>>edge[i].w;
}
sort(edge+1,edge+m+1,cmp);
init(n);
ll ans=0;
for(int i=1;i<=m;i++){
int a=find(edge[i].u);
int b=find(edge[i].v);
if(a==b)continue;
unit(a,b);
ans+=edge[i].w;
}
int x=find(1);
for(int i=1;i<=n;i++){
if(find(i)!=x){
ans=-1;
break;
}
}
cout<<ans<<endl;
return 0;
}
总结:kruskal代码看似较长,实际上思路就是简单的贪心以及与并查集的结合,多数可以用模板直接套,修改以下细节即可。
摸结束,谢谢阅读。
图论之最小生成树问题(kruskal)的更多相关文章
- 最小生成树问题---Prim算法与Kruskal算法实现(MATLAB语言实现)
2015-12-17晚,复习,甚是无聊,阅<复杂网络算法与应用>一书,得知最小生成树问题(Minimum spanning tree)问题.记之. 何为树:连通且不含圈的图称为树. 图T= ...
- 【算法】Kruskal算法(解决最小生成树问题) 含代码实现
Kruskal算法和Prim算法一样,都是求最小生成树问题的流行算法. 算法思想: Kruskal算法按照边的权值的顺序从小到大查看一遍,如果不产生圈或者重边,就把当前这条边加入到生成树中. 算法的正 ...
- 最小生成树问题------------Prim算法(TjuOj_1924_Jungle Roads)
遇到一道题,简单说就是找一个图的最小生成树,大概有两种常用的算法:Prim算法和Kruskal算法.这里先介绍Prim.随后贴出1924的算法实现代码. Prim算法 1.概览 普里姆算法(Prim算 ...
- Python小白的数学建模课-18.最小生成树问题
最小生成树(MST)是图论中的基本问题,具有广泛的实际应用,在数学建模中也经常出现. 路线设计.道路规划.官网布局.公交路线.网络设计,都可以转化为最小生成树问题,如要求总线路长度最短.材料最少.成本 ...
- 图论算法-最小费用最大流模板【EK;Dinic】
图论算法-最小费用最大流模板[EK;Dinic] EK模板 const int inf=1000000000; int n,m,s,t; struct node{int v,w,c;}; vector ...
- 最小生成树问题:Kruskal算法 AND Prim算法
Kruskal算法: void Kruskal ( ) { MST = { } ; //边的集合,最初为空集 while( Edge ...
- 最小生成树问题(Kruskal 算法)(克鲁斯卡尔)
如图就是Kuskal算法 将图中的每条边按照权值从小到大排序,每次加起来就行,注意的是不要形成回路: 重点是如何用代码实现不能形成回路 看代码; #include <cstdio> #in ...
- 最小生成树问题:kruskal算法
struct edge(int u,v,cost;};bool comp(const edge& e1,const edge& e2){ return e1.cost<e2 ...
- 图论之最小生成树之Kruskal算法
Kruskal算法,又称作为加边法,是配合并查集实现的. 图示: 如图,这是一个带权值无向图我们要求它的最小生成树. 首先,我们发现在1的所有边上,连到3的边的边权值最小,所以加上这条边. 然后在3上 ...
- 最小生成树问题---Prim算法学习
一个具有n个节点的连通图的生成树是原图的最小连通子集,它包含了n个节点和n-1条边.若砍去任一条边,则生成树变为非连通图:若增加一条边,则在图中形成一条回路.本文所写的是一个带权的无向连通图中寻求各边 ...
随机推荐
- C# const, readonly, static readonly
转: Const 定义的是静态常在对象初始化的时候赋值.以后不能改变它的值.属于编译时常量.不能用new初始化. Readonly 是只读变量.属于运行时变量.可以在类constructor里改变它的 ...
- window python 主函数 写 if __name__ == "__main__": 可以避免多进程等错误(训练yolov8时出现的)
训练yolov8时出现 类似以下错误: RunTimeError: An attempt has been made to start a new process before the current ...
- WebSocket服务
package com.sxsoft.admin.Component; import com.alibaba.fastjson.JSON; import io.netty.handler.codec. ...
- 遍历List时删除元素导致List抛出java.util.ConcurrentModificationException异常
1 public static void main(String[] args) { 2 List<String> list = new ArrayList<String>() ...
- Excel 的盒须图 离群值 Outliers
Excel 中的盒须图 翻译自https://www.excel-easy.com/examples/box-whisker-plot.html 本示例教您如何在Excel中创建盒须图.盒须图显示了数 ...
- sudo apt update 没有 Release 文件
注: 不同环境出错原因可能不同,本文仅供参考. 今天在Ubuntu 19.04 系统运行指令 sudo apt update 时,忽然提示错误,报错如下: 404 Not Found [IP: 101 ...
- python逐行读取替换文件中的字符串
用列表中的值逐行替换文件中符合条件的字符串,并保存为新的文件, open("file").readlines 方案1: 逐行替换并保存为新的文件 import re def rep ...
- 2.9 系统IO
iostream: 输入流 cin; c 指代 character 输出流 cout, cerr(立即刷新缓冲区), clog(缓冲区满后刷新) 命名空间 访问方式 namespace NameSpa ...
- 简述47种Shader Map的渲染原理与制作方法
https://mp.weixin.qq.com/s/6EVpmgC53HqklIVkIpEssg
- List集合转换成数组
我现在有个需求:将File集合转换成MultipartFile数组结构 然后我就开始在网上开启了List转换到数组之旅. 首先来看一个例子 ArrayList<String> list=n ...