最近有几位同学催我更新,于是来摸摸鱼,来讲一下最小生成树问题。

  所谓最小生成树(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

Problem Description
某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。
Input
测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。
当N为0时,输入结束,该用例不被处理。
Output
对每个测试用例,在1行里输出最小的公路总长度。
Sample Input
3
1 2 1
1 3 2
2 3 4
4
1 2 1
1 3 4
1 4 1
2 3 3
2 4 2
3 4 5
0
Sample Output
3
5
  最小生成树模板题,代码如下
#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

Constructing Roads
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 35679   Accepted: 16044

Description

There are N villages, which are numbered from 1 to N, and you should build some roads such that every two villages can connect to each other. We say two village A and B are connected, if and only if there is a road between A and B, or there exists a village C such that there is a road between A and C, and C and B are connected.

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

The first line is an integer N (3 <= N <= 100), which is the number of villages. Then come N lines, the i-th of which contains N integers, and the j-th of these N integers is the distance (the distance should be an integer within [1, 1000]) between village i and village j.

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

You should output a line contains an integer, which is the length of all the roads to be built such that all the villages are connected, and this value is minimum.

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

Bad Cowtractors
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 21954   Accepted: 8892

Description

Bessie has been hired to build a cheap internet network among Farmer John's N (2 <= N <= 1,000) barns that are conveniently numbered 1..N. FJ has already done some surveying, and found M (1 <= M <= 20,000) possible connection routes between pairs of barns. Each possible connection route has an associated cost C (1 <= C <= 100,000). Farmer John wants to spend the least amount on connecting the network; he doesn't even want to pay Bessie.

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

* Line 1: Two space-separated integers: N and M

* 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

* Line 1: A single integer, containing the price of the most expensive tree connecting all the barns. If it is not possible to connect all the barns, output -1.

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

OUTPUT DETAILS:

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)的更多相关文章

  1. 最小生成树问题---Prim算法与Kruskal算法实现(MATLAB语言实现)

    2015-12-17晚,复习,甚是无聊,阅<复杂网络算法与应用>一书,得知最小生成树问题(Minimum spanning tree)问题.记之. 何为树:连通且不含圈的图称为树. 图T= ...

  2. 【算法】Kruskal算法(解决最小生成树问题) 含代码实现

    Kruskal算法和Prim算法一样,都是求最小生成树问题的流行算法. 算法思想: Kruskal算法按照边的权值的顺序从小到大查看一遍,如果不产生圈或者重边,就把当前这条边加入到生成树中. 算法的正 ...

  3. 最小生成树问题------------Prim算法(TjuOj_1924_Jungle Roads)

    遇到一道题,简单说就是找一个图的最小生成树,大概有两种常用的算法:Prim算法和Kruskal算法.这里先介绍Prim.随后贴出1924的算法实现代码. Prim算法 1.概览 普里姆算法(Prim算 ...

  4. Python小白的数学建模课-18.最小生成树问题

    最小生成树(MST)是图论中的基本问题,具有广泛的实际应用,在数学建模中也经常出现. 路线设计.道路规划.官网布局.公交路线.网络设计,都可以转化为最小生成树问题,如要求总线路长度最短.材料最少.成本 ...

  5. 图论算法-最小费用最大流模板【EK;Dinic】

    图论算法-最小费用最大流模板[EK;Dinic] EK模板 const int inf=1000000000; int n,m,s,t; struct node{int v,w,c;}; vector ...

  6. 最小生成树问题:Kruskal算法 AND Prim算法

    Kruskal算法: void Kruskal ( ) {     MST = { } ;                           //边的集合,最初为空集     while( Edge ...

  7. 最小生成树问题(Kruskal 算法)(克鲁斯卡尔)

    如图就是Kuskal算法 将图中的每条边按照权值从小到大排序,每次加起来就行,注意的是不要形成回路: 重点是如何用代码实现不能形成回路 看代码; #include <cstdio> #in ...

  8. 最小生成树问题:kruskal算法

    struct edge(int u,v,cost;};bool comp(const edge& e1,const edge& e2){    return e1.cost<e2 ...

  9. 图论之最小生成树之Kruskal算法

    Kruskal算法,又称作为加边法,是配合并查集实现的. 图示: 如图,这是一个带权值无向图我们要求它的最小生成树. 首先,我们发现在1的所有边上,连到3的边的边权值最小,所以加上这条边. 然后在3上 ...

  10. 最小生成树问题---Prim算法学习

    一个具有n个节点的连通图的生成树是原图的最小连通子集,它包含了n个节点和n-1条边.若砍去任一条边,则生成树变为非连通图:若增加一条边,则在图中形成一条回路.本文所写的是一个带权的无向连通图中寻求各边 ...

随机推荐

  1. drush use dev.mentor.com | expecting statement

    在多站点的环境中, 不清楚在哪个目录下运行drush cc all, 这时可以运行 drush use dev.mentor.com然后还发现一个很搞笑的事情, 在一个文件的末尾一直现实红色报错符号, ...

  2. pySpark RDD基本用法

    pySpark RDD基本用法 RDD的全称是:Resilient Distributed Dataset (弹性分布式数据集),它有几个关键的特性: RDD是只读的,表示它的不可变性. 可以并行的操 ...

  3. vue-购物车的部分

    定义一个局部组件 分成三个部分 Vue.component('my-cart',{ template: ` <div class='cart'> <cart-title>< ...

  4. SQL Server【提高】事务

    事务 事务是作为单个逻辑单元执行的一系列操作,它是一个不可分割的工作逻辑单元.它包含了一组数据库操作命令,这组命令要么全部执行,要么全部不执行. 特性 原子性Atomicity 事务是一个完整的操作, ...

  5. java的Stream

    代码 List<Student> all = Student.getAll(); // 转换成数组 过滤所有的男性 Student[] students = all.stream().fi ...

  6. python--线性回归

    首先先安装要用到的包:sklearn,顾名思义机器学习包 import matplotlib.pyplot as plt import numpy as np import pandas as pd ...

  7. Json数组转List

    List<Person> list = obj.list.ToObject<List<Person>>()

  8. unity 调试 packages

    package中代码vs无法f12跳转 解决方法 1 把包copy出来 2 Package Manager->Add package from disk 3 选择包文件中的package.jso ...

  9. 91、mysql批量删除表

    ## 存储过程实现 drop PROCEDURE if EXISTS rush; create PROCEDURE rush() BEGIN ## 创建临时表,插入快照数据 drop table if ...

  10. 用Python把PDF文件转换成Word文档

    首先,下载所需要的库 1 :pdfminer   安装库命令: pip install pdfminer3k 2:  docx     安装库命令: pip install python_docx 开 ...