最小生成树(HDOJ 1863)

畅通工程

http://acm.hdu.edu.cn/showproblem.php?pid=1863

1.Prim算法:

Prim算法是由一个点(最初的集合)向外延伸,找到与集合相连权值最小的边,

然后把对应顶点拉到集合里,直到所有的顶点全部在集合里为止。

Prim算法的演示如下:

http://sjjp.tjuci.edu.cn/sjjg/DataStructure/DS/web/flashhtml/prim.htm

#include <stdio.h>
const int INF=9999999;
const int MAXV=101;
int map[MAXV][MAXV];
int res;
void Prim(int map[MAXV][MAXV],int numV)
{
    int i,j,k;
    int lowcost[MAXV];
    lowcost[1]=0;         //顶点1已经在集合里
    for(i=2;i<=numV;i++)
    {
        lowcost[i]=map[1][i];    //lowcost[]里存储的是当前集合相连的所有边的权值
    }
    for(i=2;i<=numV;i++)       //依次循环除1以外的其余点,把它拉集合里
    {
        int min=INF;
        k=1;            //k初始化为1,是为了避免下一个循环里的k=j没执行,k为任意值,lowcost[k]数组溢出
        for(j=2;j<=numV;j++)
        {
            if(lowcost[j]!=0&&lowcost[j]<min)   //查找集合外与集合相连的点
            {
                min=lowcost[j];
                k=j;
            }
        }
        res+=min;
        lowcost[k]=0;       //lowcost[k]=0表示顶点k被拉到集合里
        for(j=2;j<=numV;j++)
        {
            if(lowcost[j]!=0&&map[k][j]<lowcost[j])
            {
                lowcost[j]=map[k][j];   
            }   //顶点k加入集合后,集合发生变化,更新与集合相连边的权值
        }
    }
}
int main()
{
    int n,m,i,j;
    int begin,end,w;
    while(scanf("%d%d",&n,&m)!=EOF&&n)
    {
        for(i=1;i<=m;i++)
        {
            for(j=1;j<=m;j++)
            {
                if(i==j)
                    map[i][j]=0;
                if(i!=j)
                    map[i][j]=INF;
            }     //创建邻接矩阵
        }
        for(i=1;i<=n;i++)
        {
            scanf("%d%d%d",&begin,&end,&w);
            map[begin][end]=w;
            map[end][begin]=w;
        }
        res=0;
        Prim(map,m);
        if(res>INF)    //表示lowcost里还有INF,即图不连通
            printf("?\n");
        else
            printf("%d\n",res);
    }
    return 0;
}
View Code

2.Kruskal算法

Kruskal算法是先将所有的边按权值从小到大排序,在依次加边,

直到加了(顶点数-1)条边。注意:加边时避免出现环。

#include <stdio.h>
#include <stdlib.h>
struct node
{
    int begin;
    int end;
    int w;
};
node edge[5000];
int res,count;
int cmp(const void *p1,const void *p2)
{
    return (*(node*)p1).w-(*(node*)p2).w;
}
int Find(int *parent,int f)
{
    while(parent[f]!=f)
        f=parent[f];
    return f;
}   //寻找根节点,有点类似于并查集
void Kruskal(int numV,int numE)
{
    int n,m,i;
    int parent[101];
    for(i=1;i<=numV;i++)
        parent[i]=i;
    for(i=0;i<numE;i++)
    {
        n=Find(parent,edge[i].begin);
        m=Find(parent,edge[i].end);
        if(n!=m)
        {
            parent[n]=m;
            count++;
            res+=edge[i].w;
        }//如果一条边的两个端点的的节点相同,加上这条边后就出现环
    }
}
int main()
{   
    int n,m,i;
    while(scanf("%d%d",&n,&m)!=EOF&&n)
    {
        for(i=0;i<n;i++)
        {
            scanf("%d%d%d",&edge[i].begin,&edge[i].end,&edge[i].w);
        }
        qsort(edge,n,sizeof(edge[0]),cmp);
        res=count=0;
        Kruskal(m,n);
        if(count<m-1)    //加入的边数<顶点数-1,说明图不连通
            printf("?\n");
        else
            printf("%d\n",res);
    }
    return 0;
}
View Code

小结:Prim算法是基于顶点来加边,而Kruskal算法是基于边

原文地址:https://www.cnblogs.com/chiry/p/3234446.html