最小生成树之Prim算法的学习心得与个人总结

最小生成树之Prim算法的学习心得与个人总结

最小生成树:MST

 

从4.24到今天5.4(除去29,30,1三天假期),完成了hdu1162 , hdu1102 , hdu3371 , hdu1233 , hdu1879 , hdu1301 , hdu1864 。由于我的入手学习方法错了,导致花的时间比较长,效率低。错误方法是看完白书和黑书直接做模板题,导致了对算法细节的理解一塌糊涂。建议应该先看各种博文,理解模板每一步的意义,再做题。此处感谢TZL对我及时的批评和建议,受用无穷。

 

现在谈一下最小生成树之Prim算法。这个算法是对图中的点进行贪心,从起始点开始,到图中个点的边权值最小的纳入最小生成树集合中的过程。算法中涉及到了一个二维数组和两个一维数组,其中这两个一维数组意义重大,曾一度搞得我把算法改的一塌糊涂。

二维数组map[ a ][ b ] :存储从点a到点b的边的权值。

一维数组low[ i ] :存储记录顶点集合T'中各顶点距离到顶点集合T中最小的边的权值。这时,从起始点与for循环中到达的点i一起进行贪心,与起始点和i点相连的路径中权值最小将存进low[i]。我一开始理解成当前点所连接的边的最小权值。这是不全面的。

一维数组v[i] :记录顶点集合T'中各顶点距离到顶点集合T中哪个顶点距离最近。类型可以选择int 或者bool 。对应的值时0,1 或者true,false 。当v[i] = 1 / true 时,表示点i属于集合T 。

根据个人习惯或者题目要求,一般选择从点1或者点0开始贪心。这时对应的边界值,初始化都是不同的。一定要上下对应!

 

Prim模板:(从点1开始贪心)

以 Hdu1863 - 通畅工程 为例题,这是一道MST的裸题。

题意:n为路的数目,m为村庄数;若无法连通,则输出-1,否则输出最小权值;当n为0时,直接结束不用输出结果。

 

 

代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const int max = 0x7ffffff;
int map[105][105], v[105], low[105];
int n,m;

int MinTree(int n)
{
    int min,flag,i,j,sum;
    //初始化
    memset(v, 0, sizeof(v));
    memset(low, 0, sizeof(low));

    v[1] = 1;  //从村庄1开始贪心
    sum = 0;
    for(i=2; i<=m; i++) //此处的范围应是m村庄数,i为2~m中的一个村庄
    {
        low[i] = map[1][i];  //因为从点1开始贪心,所以是map[1][i]
    }

    for(i=2; i<=m; i++)
    //此处的范围应是m村庄数;如果是n边数的话,当最小生成树已经构建完的话,而边数小于村庄数,
    //则for会一直执行,而if则不成立,结果就是return -1.
    {
        min = max;
        flag = -1;
        //将与点1连通的边中权值最小的边找到
        for(j=1; j<=m; j++)
        {
            if(!v[j] && low[j]<min)
            {
                min = low[j];
                flag = j;
            }
        }
        if(min == max) return -1;  //此图不连通
        //sum += low[flag];
        sum += min; 
        v[flag] = 1; //标记

        for(j=1; j<=m; j++)  //更新边
        {
            if(!v[j] && low[j]>map[flag][j])
            {
                low[j] = map[flag][j];
            }
        }
    }
    return sum;  //返回最小值的和
}

int main()
{
    int i,j,a,b,c,ans;
    while(scanf("%d%d",&n,&m),n)
    {
        //初始化
        for(i=1; i<=m; i++)
            for(j=1; j<=m; j++)
            {
                if(i==j) map[i][j] = 0;
                else map[i][j] = max;
            }

        //从点a到点b的权值是c
        for(i=1; i<=n; i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(c < map[a][b])
            {
                map[a][b] = c;
                map[b][a] = c;
            }
        }
        ans = MinTree(n);
        if(ans<=0) printf("?\n");
        else printf("%d\n", ans);
    }
    return 0;
}


 

 

 

 

你可能感兴趣的:(最小生成树之Prim算法的学习心得与个人总结)