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

  所谓最小生成树(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. 2023 01 19 HW

    2023 01 19 HW Okay, then let's start.  Okay. Maybe Karina, we start with the C2 design freeze. Yeah, ...

  2. android root app 无法umount

    app已经有root权限了. 在执行umount /sbin时候总是不成功,但是在adb 的shell 里是可以的. 研究半天后发现,原来app的mount 空间被修改了. 用以下方法解决问. ech ...

  3. 第三周day1

    第三周day1,星期一 所花时间(上课时间+练习时间):6h 代码量:110 博客量:2 所学到的知识:学了一些Map的相关知识,还有如何切割字符串,如何将字符串转换成字符数组str.toCharAr ...

  4. Fedora 切换为阿里软件源

    1.备份原软件源配置 cp /etc/yum.repos.d/fedora.repo /etc/yum.repos.d/fedora.repo.bak cp /etc/yum.repos.d/fedo ...

  5. mysql添加到环境变量

    今天换新系统,以前的一些常用软件重新安装了一下,安装到mysql我还是按照以前的习惯选择了低版本的5.7系列,突然想要装一把,像python一样可以直接访问解释器,能不能直接在cmd中输入mysql就 ...

  6. wamp+phpstrom+Xdebuge helper(google)

    一.软件安装两个软件的安装和第三个浏览器插件就不再赘述,网上有很多详细的教程,自行百度. 二.配置步骤1.wampwamp的优势在于自带xdebuge的dll文件,所以不需要在官网根据版本下载,具体位 ...

  7. ubuntu 铺满桌面 调整字体 安装firefox中文版

    ubuntu 铺满桌面 安装 open-vm-tools sudo apt-get install open-vm-tools sudo apt-get install open-vm* . rebo ...

  8. C语言学习--文件操作--文件流指针--打开文件

    当打开一个文件时, 系统会返回一个结构体, 这个结构体有对此文件操作的所有信息 调用fopen时,系统返回这个结构体的地址 FILE *p = fopen("a.txt") 打开一 ...

  9. K8S informer机制

    一.informer介绍 Kubernetes基于声明式API的设计理念,所谓声明式API,即告诉Kubernetes Controller资源对象的期望状态,这样为Kubernetes在事件通知后, ...

  10. 易语言json

    1.易语言助手  ->网页功能->网页调试 打开post工具 提交协议头里面 右键可以自定义默认的十几个ua头 2.项目爬模发送数据格式为[{},{},{},{}]  相当于没有键 只有值 ...