您的位置:首页 > 其它

最小生成树(一)

2016-05-04 20:15 141 查看

简介

在一个无向连通图中,如果存在一个连通子图包含原图中所有的节点和部分边,且这个子图不存在回路,那么我们称这个子图为原图的一棵生成树。在带权图中,所有的生成树中边权最小的一课或者几棵称为最小生成树。

Kruskal算法

算法思想

Kruskal算法比较简单, 实际上是一种贪心的方式。不断从未选的边的集合当中选择权值最小的,并且不和已选的点(初始已选的点的集合是空集)构成回路的边,将边的两个端点加入我们的已选点的集合当中,如此往复。

支撑这个算法有个小的定理,我们可以描述为:

在要求解得连通图中,任选一些点属于集合A,剩余的点属于集合B,那么我们选择一条权值最小的边,有个条件是这条边的两个顶点分属于集合A和集合B,那么这条边一定包含于这个连通图的一棵最小生成树当中。

证明可以使用替换法。

算法操作

构建点集(if necessary)。

构建边集。

按照边的权值大小进行排序。

使用并查集检查合并集合。

如何判断我们选择的边已经在已选的集合中了呢?如果我们选择的边的集合的两个端点在集合中,那么这条边一定和原来的集合构成回路,因此这条边我们是不能选择的。只需要判断roota和rootb关系即可,和之前的不矛盾。

下面两道题目练习一下,第一道是很明显的题目,练手用。

传送门:还是畅通工程

#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
#include <algorithm>
#define N 6000
using namespace std;

//最小生成树
struct Edge{
int a,b;
int length;
bool operator < (const Edge &B)const{
return length<B.length;
}
}edge
;

int Tree
;  //并查集处理集合问题
int findRoot(int x){
if(Tree[x]==-1)
return x;
else{
int root=findRoot(Tree[x]);
Tree[x]=root;
return root;
}
}
int main(){
//freopen("test.txt","r",stdin);
int n;
while(scanf("%d",&n)!=EOF &&n!=0){
memset(Tree,-1,sizeof(Tree));
memset(edge,0,sizeof(edge));
for(int i=1;i<=n*(n-1)/2;i++){
scanf("%d%d%d",&edge[i].a,&edge[i].b,&edge[i].length);
}
sort(edge+1,edge+1+n*(n-1)/2);
int re=0;
for(int i=1;i<=n*(n-1)/2;i++){
int roota=findRoot(edge[i].a);
int rootb=findRoot(edge[i].b);
if(roota!=rootb){
re+=edge[i].length;
Tree[roota]=rootb;
}
}
printf("%d\n",re);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息