zoukankan      html  css  js  c++  java
  • 最小生成树之 prim算法和kruskal算法(以 hdu 1863为例)

    最小生成树的性质

    MST性质:设G = (V,E)是连通带权图,U是V的真子集。假设(u,v)∈E,且u∈U,v∈V-U,且在全部这种边中,

    (u,v)的权c[u][v]最小,那么一定存在G的一棵最小生成树,(u,v)为当中一条边。

    构造最小生成树,要解决下面两个问题:
    (1).尽可能选取权值小的边,但不能构成回路(也就是环)。
    (2).选取n-1条恰当的边以连接网的n个顶点。

    Prim算法的思想:

    设G = (V,E)是连通带权图,V = {1,2,…,n}。先任选一点(一般选第一个点),首先置S = {1},然后,仅仅要S是V的真子集,就选取满足条件i ∈S,j ∈V-S,且c[i][j]最小的边,将顶点j加入到S中。这个过程一直进行到S = V时为止。在这个过程中选取到的全部边恰好构成G的一棵最小生成树。

    Prim算法代码     

    以 hdu 1863为例 (点击打开链接

    #include<stdio.h>
    #include<limits.h>
    #include<string.h>
    #define N 100
    int n,m,map[N+5][N+5],v[N+5],low[N+5];
    int prim()
    {
        int i,j,pos,min,s=0;
        memset(v,0,sizeof(v));           //v[i]用来标记i是否已訪问,先初始化为0,表示都未訪问
        v[1]=1;                         //先任选一点作为第一个点
        pos=1;                          //pos用来标记当前选的点的下标
        for(i=2;i<=n;i++)
            low[i]=map[1][i];          //用low数组存已选点到其它点的权值
        for(i=1;i<n;i++){
            min=INT_MAX;
            for(j=1;j<=n;j++)                //求权值最小的边
                if(!v[j]&&low[j]<min){
                    min=low[j];
                    pos=j;                 
                }
            if(min==INT_MAX)    
                break;
            s+=min;            
            v[pos]=1;           
            for(j=1;j<=n;j++)                   //更新low数组    
                if(!v[j]&&map[pos][j]<low[j])
                    low[j]=map[pos][j];
        }
        if(i!=n)
            s=-1;
        return s;
    }
    int main()
    {
        int i,j,s,a,b,c;
        while(scanf("%d%d",&m,&n)!=EOF){          //m为道路数,n为村庄数
            if(m==0)
                break;
            for(i=1;i<=n;i++)
                for(j=1;j<=n;j++)
                    map[i][j]=INT_MAX;             //先将map数组初始化为非常大的值(int 最大值)
            for(i=1;i<=m;i++){
                scanf("%d%d%d",&a,&b,&c);
                map[a][b]=map[b][a]=c;             //map[a][b]存的从a到b的权值
            }
            s=prim();
            if(s==-1)
                printf("?
    ");
            else
                printf("%d
    ",s);
        }
        return 0;
    }
    

    
    

    Kruskal算法思想

    给定无向连同带权图G = (V,E),V = {1,2,...,n}。

    (1)首先将G的n个顶点看成n个孤立的连通分支。将全部的边按权从小大排序。

    (2)从第一条边開始,依边权递增的顺序检查每一条边。并依照下述方法连接两个不同的连通分支:当查看到第k条边(v,w)时,假设端点v和w各自是当前两个不同的连通分支T1和T2的端点是,就用边(v,w)将T1和T2连接成一个连通分支,然后继续查看第k+1条边;假设端点v和w在当前的同一个连通分支中,就直接再查看k+1条边。这个过程一个进行到仅仅剩下一个连通分支时为止。此时,已构成G的一棵最小生成树。

    Kruskal算法代码:

    以 hdu 1863为例 (点击打开链接

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int f[105],n,m;
    struct stu
    {
        int a,b,c;
    }t[5500];
    int cmp(struct stu x,struct stu y)
    {
        return x.c<y.c;
    }
    int find(int x)              //路径压缩,找父节点
    {
        if(x!=f[x])
            f[x]=find(f[x]);
        return f[x];
    }
    int krus()
    {
        int i,k=0,s=0,x,y;
        for(i=1;i<=n;i++){
            x=find(t[i].a);
            y=find(t[i].b);
            if(x!=y){              //最小生成树不能形成环,所以要推断它们的是否属于同一集合
                s+=t[i].c;
                k++;
                if(k==m-1)      //最小生成树会形成m-1(顶点-1)条边,若已形成,则最小生成树已构成
                    break;
                f[x]=y;          //将父节点更新
            }
        }
        if(k!=m-1)
            s=-1;
        return s;
    }
    int main()
    {
        int i,s;
        while(scanf("%d%d",&n,&m)!=EOF){
            if(n==0)
                break;
            for(i=1;i<=n;i++)
                scanf("%d%d%d",&t[i].a,&t[i].b,&t[i].c);
            for(i=1;i<=m;i++)         //f[i]存的结点i的父亲,先将其父亲都初始化为其本身
                f[i]=i;
            sort(t+1,t+1+n,cmp);      //按权值从小到大排序
            s=krus();
            if(s==-1)
                printf("?
    ");
            else
                printf("%d
    ",s);
        }
        return 0;
    }

    
    

    注:若顶点数为n,边为e

    prim算法适合稠密图,其时间复杂度为O(n^2),其时间复杂度与边的数目无关,

    kruskal算法的时间复杂度为O(eloge)跟边的数目有关,适合稀疏图。



  • 相关阅读:
    Windows Phone 7 LongListSelector控件实现分类列表和字母索引
    Windows Phone 7 自定义弹出窗口
    Windows 8 异步编程
    Windows Phone 7 Http请求添加Cookie的方法
    XNA游戏:软键盘弹窗输入
    Windows Phone 8 手机存储卡数据
    Windows Phone 7 框架和页面
    Windows Phone 8 发音合成与语音识别
    Windows 8 Hello World
    Windows Phone 8 程序联系人存储
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/3945682.html
Copyright © 2011-2022 走看看