什么是最小生成树(MST)?

给定一个带权的无向连通图,选取一棵生成树(原图的极小连通子图),使生成树上所有边上权的总和为最小,称为该图的最小生成树。

求解最小生成树的算法一般有这两种:Prim算法和Kruskal算法。

Prim算法(普里姆算法)

图的存贮结构采用邻接矩阵。此方法是按各个顶点连通的步骤进行,需要用一个顶点集合,开始为空集,以后将以连通的顶点陆续加入到集合中,全部顶点加入集合后就得到所需的最小生成树。

简单描述:

  1.初始化:Vnew = {x},其中x为集合V中的任一节点(作为起始点),Enew = {},为空。

  2.在边集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一)。将v加入集合Vnew中,将<u, v>边加入集合Enew中。

  3.重复操作2直至Vnew = V。

代码展示:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std; const int INF=0x3f3f3f3f;
const int N=; int G[N][N];//邻接矩阵
int Lowest[N];//表示和已选顶点集Vnew的最小距离,Lowest[i]=0表示点i已经在Vnew中
int n,m; int prim()
{
int Num=;//最小生成树权值 for(int i=;i<=n;i++)//选取第一个点开始
Lowest[i]=G[][i];//取第一行权值 for(int i=;i<n;i++)//找到新顶点加入(n-1个)
{
int minid=;
int mindis=INF;
for(int j=;j<=n;j++)//找到距离最小的
{
if(Lowest[j]!=&&Lowest[j]<mindis)
{
mindis=Lowest[j];
minid=j;
}
}
Num+=mindis;
Lowest[minid]=;//把点minid加入Vnew for(int j=;j<=n;j++)//更新Lowest数组
if(Lowest[j]>G[minid][j])
Lowest[j]=G[minid][j];
}
return Num;
} int main()
{
while(~scanf("%d%d",&n,&m))
{
memset(G,0x3f,sizeof(G));//初始化为最大值
for(int i=;i<=n;i++)//对角线为0
G[i][i]=; int u,v,w;
for(int i=;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
G[u][v]=G[v][u]=w;
} int MST=prim();//计算最小生成树总权值 printf("%d\n",MST);
}
}

Kruskal算法(克鲁斯卡尔算法

图的存贮结构采用边集数组,且权值相等的边在数组中排列次序可以是任意的。该方法对于边相对比较多的不是很实用,浪费时间。思想是贪心思想。

方法:将图中的边按其权值由小到大的次序顺序选取,若选边后不形成回路,则保留作为一条边,若形成回路则除去。依次选够(n-1)条边,即得最小生成树。(n为顶点数)

首先我们把所有的边按照权值先从小到大排列,接着按照顺序选取每条边,如果这条边的两个端点不属于同一集合,那么就将它们合并,直到所有的点都属于同一个集合为止。至于怎么合并到一个集合,那么这里我们就可以用到一个工具——-并查集(不知道的同学请移步:Here)。换而言之,Kruskal算法就是基于并查集的贪心算法。

代码展示:

#include <cstdio>
#include <cstdlib>
#define MAXN 10010
using namespace std; int Uset[MAXN];//并查集
int Rank[MAXN];//秩
typedef struct{
int a, b, price;
}Node;
Node edge[MAXN]; int cmp(const void*a, const void *b){
return ((Node*)a)->price - ((Node*)b)->price;
} void Init(int n)//并查集初始化
{
for(int i = ; i < n; i++)
{
Rank[i] = ;
Uset[i] = i;
}
} int find(int x)
{
int root = x;
while(root != Uset[root]) root = Uset[root];
while(x != root)
{
int t = Uset[x];
Uset[x] = root;
x = t;
}
return root;
} void unionSet(int x, int y)
{
x = find(x);
y = find(y);
if(Rank[x] > Rank[y])
Uset[y] = x;
else {
Uset[x] = y;
if(Rank[x] == Rank[y]) Rank[y]++;
}
} int Kruskal(int n, int m)
{
int nEdge = , res = ; qsort(edge, m, sizeof(edge[]), cmp);//将边按照权值从小到大排序
for(int i = ; i < m && nEdge != n - ; i++)
{
if(find(edge[i].a) != find(edge[i].b))//判断当前这条边的两个端点是否属于同一棵树
{
unionSet(edge[i].a, edge[i].b);
res += edge[i].price;
nEdge++;
}
}
//如果加入边的数量小于m - 1,则表明该无向图不连通,等价于不存在最小生成树
if(nEdge < n-) res = -;
return res;
}
int main()
{
int n, m, ans;//n为村庄的数量,m为边的数量
while(scanf("%d%d", &n, &m)&&n)
{
Init(n);
for(int i = ; i < m; i++)
scanf("%d%d%d", &edge[i].a, &edge[i].b, &edge[i].price); ans = Kruskal(n, m);
if(ans == -) printf("?\n");
else printf("%d\n", ans);
}
return ;
}

另外,可以参考:http://blog.csdn.net/luomingjun12315/article/details/47700237

作者: AlvinZH

出处: http://www.cnblogs.com/AlvinZH/

本人Github:https://github.com/Pacsiy/JobDu

本文版权归作者AlvinZH和博客园所有,欢迎转载和商用,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

最小生成树(Prim算法+Kruskal算法)的更多相关文章

  1. 无向带权图的最小生成树算法——Prim及Kruskal算法思路

    边赋以权值的图称为网或带权图,带权图的生成树也是带权的,生成树T各边的权值总和称为该树的权. 最小生成树(MST):权值最小的生成树. 生成树和最小生成树的应用:要连通n个城市需要n-1条边线路.可以 ...

  2. [数据结构]最小生成树算法Prim和Kruskal算法

    最小生成树 在含有n个顶点的连通图中选择n-1条边,构成一棵极小连通子图,并使该连通子图中n-1条边上权值之和达到最小,则称其为连通网的最小生成树.  例如,对于如上图G4所示的连通网可以有多棵权值总 ...

  3. 图的最小生成树的理解和实现:Prim和Kruskal算法

    最小生成树 一个连通图的生成树是一个极小的连通子图,它含有图中所有的顶点,但只有足以构成一棵树的n-1条边.我们将构造连通网的最小代价生成树称为最小生成树(Minimum Cost Spanning ...

  4. hdu 1233 还是畅通工程 最小生成树(prim算法 + kruskal算法)

    还是畅通工程                                                                            Time Limit: 4000/2 ...

  5. 最小生成树 Prim算法 Kruskal算法实现

    最小生成树定义 最小生成树是一副连通加权无向图中一棵权值最小的生成树. 在一给定的无向图 G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即,而 w(u, v) 代表此边的 ...

  6. 最小生成树Prim算法 Kruskal算法

    Prim算法(贪心策略)N^2 选定图中任意定点v0,从v0开始生成最小生成树 树中节点Va,树外节点Vb 最开始选一个点为Va,其余Vb, 之后不断加Vb到Va最短距离的点 1.初始化d[v0]=0 ...

  7. 算法(图论)——最小生成树及其题目应用(prim和Kruskal算法实现)

    题目 n个村庄间架设通信线路,每个村庄间的距离不同,如何架设最节省开销? Kruskal算法 特点 适用于稀疏图,时间复杂度 是nlogn的. 核心思想 从小到大选取不会产生环的边. 代码实现 代码中 ...

  8. 【431】Prim 算法 & Kruskal 算法

    Prim 算法: Minimum Spanning Tree(MST):最小生成树,就是连接所有节点的最小权值 mst集合与rest集合 mst集合中顶点,找到一条最小权值的边 然后把边相关的顶点,选 ...

  9. 最小生成树之算法记录【prime算法+Kruskal算法】【模板】

    首先说一下什么是树: 1.只含一个根节点 2.任意两个节点之间只能有一条或者没有线相连 3.任意两个节点之间都可以通过别的节点间接相连 4.除了根节点没一个节点都只有唯一的一个父节点 5.也有可能是空 ...

随机推荐

  1. 钉钉开发笔记(5)android系统中html软键盘的适配

    最近项目中发现个别Android手机中存在弹出的软键盘会遮挡输入框的现象,最后自己写了一个方法(如下),问题基本解决. 记录下来,防止忘记.有什么不对的地方欢迎指正.O(∩_∩)O 1 //键盘适配 ...

  2. eclipse+hbase开发环境部署

    一.前言 1. 前提 因为hbase的运行模式是伪分布式,需要用到hdfs,所以在此之前,我已经完成了hadoop-eclipse的开发环境搭建,详细看另一篇文章:hadoop开发环境部署——通过ec ...

  3. shiro 权限集成Ehcache 配置 学习记录(二)

    1.加入依赖 <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-eh ...

  4. windows下 apache,php,mysql,phpadmin集成化安装

    1.appserv 直接下载安装, 2.linux环境下下载安装LAMP

  5. archives of source

    "ubuntu 暂时不能解析域名 archive.ubuntu.com"怎么办? root下输入命令:lsb_release -a用来查询Ubuntu版本号 登录网站 http:/ ...

  6. Git自动补全

    一.简介 假使你使用命令行工具运行Git命令,那么每次手动输入各种命令是一件很令人厌烦的事情.为了解决这个问题,你可以启用Git的自动补全功能,完成这项工作仅需要几分钟.   二.操作步骤 1) cd ...

  7. Spring.net方法的替换

    .为什么有时候你再执行某个方法的时候比如某个操作 a.权限验证 b.任务执行 当我执行到这个方法的时候,我可以先验证权限,如果验证不通过则替换到另一个方法去执行 public class MyValu ...

  8. 路飞项目背景,contentType以及django缓存

    昨日回顾: 分页器: 普通分页 # 普通分页 from rest_framework.pagination import PageNumberPagination -每页的大小(默认) -查询的时候, ...

  9. (2)WePHP 控制器与使用模板

    <?php class C_index extends Action { public function __initialize() { echo"自动执行"; } pub ...

  10. HDU 4586 Play the Dice (数学,概率,等比公式,极限)

    题意:给你一个n面的骰子每个面有一个值,然后其中有不同值代表你能获得的钱,然后有m个特殊的面,当你骰到这一面的时候可以获得一个新的机会 问你能得到钱的期望. 析: 骰第一次     sum/n 骰第二 ...