最小生成树

参考:勿在浮沙筑高台:http://blog.csdn.net/luoshixian099/article/details/51908175

qwureyhttp://blog.csdn.net/yeruby/article/details/38615045

关于图的几个概念定义:

  • 连通图:在无向图中,若任意两个顶点 vi vj 都有路径相通,则称该无向图为连通图。
  • 强连通图:在有向图中,若任意两个顶点 vi vj 都有路径相通,则称该有向图为强连通图。
  • 连通网:在连通图中,若图的边具有一定的意义,每一条边都对应着一个数,称为权;权代表着连接连个顶点的代价,称这种连通图叫做连通网。
  • 生成树:一个连通图的生成树是指一个连通子图,它含有图中全部n个顶点,但只有足以构成一棵树的n-1条边。一颗有n个顶点的生成树有且仅有n-1条边,如果生成树中再添加一条边,则必定成环。
  • 最小生成树:在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树。 
    这里写图片描述

下面介绍两种求最小生成树算法

1.Kruskal算法

此算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。 
1. 把图中的所有边按代价从小到大排序; 
2. 把图中的n个顶点看成独立的n棵树组成的森林; 
3. 按权值从小到大选择边,所选的边连接的两个顶点 ui,vi ,应属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。 
4. 重复(3),直到所有顶点都在一颗树内或者有n-1条边为止。

这里写图片描述

2.Prim算法

此算法可以称为“加点法”,每次迭代选择代价最小的边对应的点,加入到最小生成树中。算法从某一个顶点s开始,逐渐长大覆盖整个连通网的所有顶点。

  1. 图的所有顶点集合为 V ;初始令集合 u={s},v=Vu ;
  2. 在两个集合 u,v 能够组成的边中,选择一条代价最小的边 (u0,v0) ,加入到最小生成树中,并把 v0 并入到集合u中。
  3. 重复上述步骤,直到最小生成树有n-1条边或者n个顶点为止。

由于不断向集合u中加点,所以最小代价边必须同步更新;需要建立一个辅助数组closedge,用来维护集合v中每个顶点与集合u中最小代价边信息,:

struct
{
  char vertexData   //表示u中顶点信息
  UINT lowestcost   //最小代价
}closedge[vexCounts]
 
 
  • 1
  • 2
  • 3
  • 4
  • 5

这里写图片描述

还是prime:

MST(Minimum Spanning Tree,最小生成树)问题有两种通用的解法,Prim算法就是其中之一,它是从点的方面考虑构建一颗MST,大致思想是:设图G顶点集合为U,首先任意选择图G中的一点作为起始点a,将该点加入集合V,再从集合U-V中找到另一点b使得点b到V中任意一点的权值最小,此时将b点也加入集合V;以此类推,现在的集合V={a,b},再从集合U-V中找到另一点c使得点c到V中任意一点的权值最小,此时将c点加入集合V,直至所有顶点全部被加入V,此时就构建出了一颗MST。因为有N个顶点,所以该MST就有N-1条边,每一次向集合V中加入一个点,就意味着找到一条MST的边。


用图示和代码说明:

初始状态:


设置2个数据结构:

lowcost[i]:表示以i为终点的边的最小权值,当lowcost[i]=0说明以i为终点的边的最小权值=0,也就是表示i点加入了MST

mst[i]:表示对应lowcost[i]的起点,即说明边<mst[i],i>是MST的一条边,当mst[i]=0表示起点i加入MST


我们假设V1是起始点,进行初始化(*代表无限大,即无通路):


lowcost[2]=6lowcost[3]=1lowcost[4]=5lowcost[5]=*,lowcost[6]=*

mst[2]=1mst[3]=1,mst[4]=1mst[5]=1,mst[6]=1(所有点默认起点是V1)


明显看出,以V3为终点的边的权值最小=1,所以边<mst[3],3>=1加入MST


此时,因为点V3的加入,需要更新lowcost数组和mst数组:

lowcost[2]=5lowcost[3]=0lowcost[4]=5lowcost[5]=6,lowcost[6]=4

mst[2]=3mst[3]=0,mst[4]=1mst[5]=3,mst[6]=3


明显看出,以V6为终点的边的权值最小=4,所以边<mst[6],6>=4加入MST


此时,因为点V6的加入,需要更新lowcost数组和mst数组:

lowcost[2]=5lowcost[3]=0lowcost[4]=2lowcost[5]=6lowcost[6]=0

mst[2]=3mst[3]=0,mst[4]=6mst[5]=3,mst[6]=0


明显看出,以V4为终点的边的权值最小=2,所以边<mst[4],4>=4加入MST


此时,因为点V4的加入,需要更新lowcost数组和mst数组:

lowcost[2]=5,lowcost[3]=0,lowcost[4]=0,lowcost[5]=6lowcost[6]=0

mst[2]=3,mst[3]=0,mst[4]=0mst[5]=3mst[6]=0


明显看出,以V2为终点的边的权值最小=5,所以边<mst[2],2>=5加入MST


此时,因为点V2的加入,需要更新lowcost数组和mst数组:

lowcost[2]=0,lowcost[3]=0,lowcost[4]=0,lowcost[5]=3,lowcost[6]=0

mst[2]=0,mst[3]=0,mst[4]=0mst[5]=2mst[6]=0


很明显,以V5为终点的边的权值最小=3,所以边<mst[5],5>=3加入MST

lowcost[2]=0,lowcost[3]=0lowcost[4]=0,lowcost[5]=0lowcost[6]=0

mst[2]=0,mst[3]=0mst[4]=0,mst[5]=0mst[6]=0


至此,MST构建成功,如图所示:

例题:hdu1233 还是畅通工程


prime   ac代码:

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<math.h>
#include<string.h>
using namespace std;
#define INF 0x3f3f3f3f

int cost[1005][1005];//cost表示边e=(u,v)的权值
int mincost[1005];//最小权值
bool used[1005];//顶点i是否包含在集合x中
int V;//顶点数

int prime(){
    for(int i=0;i<V;i++)
    {
        mincost[i]=INF;
        used[i]=false;
    }
    mincost[0]=0;
    int res=0;
    int v=0;
    while(v!=-1)
    {
        v=-1;
        //从不属于x的顶点中选取从x到其权值最小的顶点
        for(int u=0;u<V;u++)
        {
            if(!used[u]&&(v==-1||mincost[u]<mincost[v]))
                v=u;
        }
        used[v]=true;//把顶点v加进x
        res+=mincost[v];//把边加进结果里
        for(int u=0;u<V;u++){
            mincost[u]=min(mincost[u],cost[v][u]);
        }
    }
    return res;
}
int main()
{
    while(~scanf("%d",&V)&&V!=0)
    {
        for(int i=0;i<V*(V-1)/2;i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            cost[a-1][b-1]=c;
            cost[b-1][a-1]=c;
        }
        printf("%d\n",prime());
    }
    return 0;
}
Kruskal ac代码:

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=10005;
struct node { int from,to,len;} edge[maxn];//储存边的数据结构
int n,fa[maxn],m,ans,q;
bool cmp(node a,node b) { return a.len<b.len; }//边按从小到大的顺序排列
int Find(int x)
{
    if(fa[x]==x) return x;
    return fa[x]=Find(fa[x]);
}
void Merge(int x,int y)
{
    x=Find(x),y=Find(y);
    if(x!=y) fa[y]=x;
}
int kruskal()
{
    sort(edge,edge+m,cmp);//边排序
    for(int i=0;i<=n;i++) fa[i]=i;//初始化并查集
    ans=0;
    for(int i=0;i<m;i++)//一条边的两个端点不在同一个集合,则选它,并合并端点
        if(Find(edge[i].from)!=Find(edge[i].to)) Merge(edge[i].from,edge[i].to),ans+=edge[i].len;
    return ans;
}
int main()
{
    while(cin>>n,n)
    {
        m=n*(n-1)/2;
        for(int i=0;i<m;i++) cin>>edge[i].from>>edge[i].to>>edge[i].len;
        cout<<kruskal()<<endl;
    }
    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值