码迷,mamicode.com
首页 > 编程语言 > 详细

caioj1090最小生成树之kruskal算法【模板元问题】

时间:2017-10-07 12:34:51      阅读:248      评论:0      收藏:0      [点我收藏+]

标签:std   运算   原来   不清楚   clu   距离   idt   端点   相同   

首先总结一下概念:

什么是最小生成树呢?

一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。

个人认为kruskal算法还是比较优秀的。

所以此篇重点讨论kruskal算法。

1.概览

Kruskal算法是一种用来寻找最小生成树的算法,由Joseph Kruskal在1956年发表。用来解决同样问题的还有Prim算法和Boruvka算法等。三种算法都是贪婪算法的应用。和Boruvka算法不同的地方是,Kruskal算法在图中存在相同权值的边时也有效。

 

图例描述:

技术分享首先第一步,我们有一张图Graph,有若干点和边 

技术分享

 

将所有的边的长度排序,用排序的结果作为我们选择边的依据。这里再次体现了贪心算法的思想。资源排序,对局部最优的资源进行选择,排序完成后,我们率先选择了边AD。这样我们的图就变成了右图

 

 

 

 

技术分享在剩下的变中寻找。我们找到了CE。这里边的权重也是5

技术分享依次类推我们找到了6,7,7,即DF,AB,BE。

技术分享

下面继续选择, BC或者EF尽管现在长度为8的边是最小的未选择的边。但是现在他们已经连通了(对于BC可以通过CE,EB来连接,类似的EF可以通过EB,BA,AD,DF来接连)。所以不需要选择他们。类似的BD也已经连通了(这里上图的连通线用红色表示了)。

最后就剩下EG和FG了。当然我们选择了EG。最后成功的图就是右:

 

 

 

 

3.简单证明Kruskal算法

对图的顶点数n做归纳,证明Kruskal算法对任意n阶图适用。

归纳基础:

n=1,显然能够找到最小生成树。

归纳过程:

假设Kruskal算法对n≤k阶图适用,那么,在k+1阶图G中,我们把最短边的两个端点a和b做一个合并操作,即把u与v合为一个点v‘,把原来接在u和v的边都接到v‘上去,这样就能够得到一个k阶图G‘(u,v的合并是k+1少一条边),G‘最小生成树T‘可以用Kruskal算法得到。

我们证明T‘+{<u,v>}是G的最小生成树。

用反证法,如果T‘+{<u,v>}不是最小生成树,最小生成树是T,即W(T)<W(T‘+{<u,v>})。显然T应该包含<u,v>,否则,可以用<u,v>加入到T中,形成一个环,删除环上原有的任意一条边,形成一棵更小权值的生成树。而T-{<u,v>},是G‘的生成树。所以W(T-{<u,v>})<=W(T‘),也就是W(T)<=W(T‘)+W(<u,v>)=W(T‘+{<u,v>}),产生了矛盾。于是假设不成立,T‘+{<u,v>}是G的最小生成树,Kruskal算法对k+1阶图也适用。

由数学归纳法,Kruskal算法得证。

 

首先提示一下,毕竟是棵树,我们决不能让它形成环,所以要引入并查集。当发现一条路径,判断如果两个点的祖先已经一样了,说明这两个点之前已被纳入最小生成的树的范畴,纳入则成环,故此时不将其并入并查集(若并查集概念不清楚的可以访问https://baike.baidu.com/item/%E5%B9%B6%E6%9F%A5%E9%9B%86/9388442?fr=aladdin,这个很容易弄清楚)。

还是附一下题:

1090: [视频]最小生成树(模版 kruskal算法 元问题by scy)

时间限制: 1 Sec  内存限制: 128 MB

题目描述

 

【题目描述】 一个有n个点的连通无向图,有m条无向边,每条边有一个长度c, 如果连接所有点,只需要从m条无向边中选n-1条,为什么? 现在要求这n-1条边的长度和最小。

以上就是最小生成树的概念。

【输入格式】  第一行输入 n和 m (1<=n<=1000,n-1<=m<=50 0000) 下来 m 行,每行三个数 x,y,c,表示点 x 和 点 y 有一条距离为 c 的无向边。0<c<=20 【输出格式】 一行一个整数,即连接n个点的最小长度和。 【样例1输入】 5 5 1 2 1 2 3 1 3 4 2 4 5 1 5 1 1 【样例1输出】 4 【样例2输入】 7 9 1 6 5 5 1 6 7 4 1 6 7 8 5 3 2 4 5 3 4 2 4 2 3 5 3 1 7 【样例2输出】 21

 

接下来我们用代码说话:

 

#include<cstdio>
#include<algorithm>
using namespace std;
struct edge{//结构体来存边的信息
  int a,b,c;
  // bool operator < (const edge &x) const 重载<运算符,当然为了结构体的比较你在外面写函数也行
  // { return c<x.c; }
}e[500000];//边的信息比较多,这里不要吃亏!开大一点
int fa[1005];//并查集

int n,m;

bool cmp(const edge &x1,const edge &x2)
{
  return x1.c<x2.c;
}

int find(int x)//找x的老祖先
{
  if (fa[x]!=x) return fa[x]=find(fa[x]);//都是套路,递归压缩路径防超时
  return fa[x];
}

int main()
{
  scanf("%d%d",&n,&m);
  for (int i=1;i<=n;i++) fa[i]=i;//并查集的初始化
  for (int i=1;i<=m;i++) scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].c);
  sort(e+1,e+m+1,cmp);
  int t=0;//计边数
  int ans=0;//计答案

  for (int i=1;i<=m;i++)
  {
    int k1=find(e[i].a);
    int k2=find(e[i].b);
    if (k1!=k2)//太好了!说明可以合并!
    {
      fa[k2]=k1;
      ans+=e[i].c;
      t++;
      if (t==n-1) break;
    }
  }
  printf("%d\n",ans);
  return 0;
}

caioj1090最小生成树之kruskal算法【模板元问题】

标签:std   运算   原来   不清楚   clu   距离   idt   端点   相同   

原文地址:http://www.cnblogs.com/powerfulbee/p/7633225.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!