最小生成树之 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为例 (点击打开链接

<pre name="code" class="cpp">#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("?\n");
        else
            printf("%d\n",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为例 (点击打开链接

<pre name="code" class="cpp">#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)      //<span style="font-family: KaiTi_GB2312;">最小生成树会形成m-1(顶点-1)条边,若已形成,则最小生成树已构成</span>
                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("?\n");
        else
            printf("%d\n",s);
    }
    return 0;
}

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

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

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

最小生成树之 prim算法和kruskal算法(以 hdu 1863为例),布布扣,bubuko.com

时间: 2024-12-28 18:17:57

最小生成树之 prim算法和kruskal算法(以 hdu 1863为例)的相关文章

最小生成树:prim算法和kruskal算法

一个连通图的生成树是图的极小连通子图.它包含图中的所有顶点,并且只含尽可能少的边.若砍去它的一条边,就会使生成树变成非连通图:若给它增加一条边,则会形成一条回路. 最小生成树有如下性质: 1.最小生成树非唯一,可能有多个最小生成树: 2.最小生成树的边的权值之和总唯一,而且是最小的: 3.最小生成树的边数为顶点数减1. 构造最小生成树可以有多种算法.其中多数算法利用了最小生成树的下列一种简称为MST的性质: 假设N=(V,{E})是一个连通网,U是顶点集V的一个非空子集.若(u, v)是一条具有

java实现最小生成树的prim算法和kruskal算法

在边赋权图中,权值总和最小的生成树称为最小生成树.构造最小生成树有两种算法,分别是prim算法和kruskal算法.在边赋权图中,如下图所示: 在上述赋权图中,可以看到图的顶点编号和顶点之间邻接边的权值,若要以上图来构建最小生成树.结果应该如下所示: 这样构建的最小生成树的权值总和最小,为17 在构建最小生成树中,一般有两种算法,prim算法和kruskal算法 在prim算法中,通过加入最小邻接边的方法来建立最小生成树算法.首先构造一个零图,在选一个初始顶点加入到新集合中,然后分别在原先的顶点

最小生成树(Prim算法和Kruskal算法)

1)最小生成树 给定一个无向图,如果它的某个子图中任意两个顶点都互相连通并且是一棵树,那么这棵树就叫生成树.如果边上有权值,那么使得边权和最小的生成树叫做最小生成树(MST,Minimum Spanning Tree) 2)应用 比如让你为一个镇的九个村庄架设通信网络,每个村庄相当于一个顶点,权值是村与村之间可通达的直线距离,要求你必须用最小的成本完成这次任务:或者村庄之间建公路,连通N个村庄要N-1条路,如何让建路成本最低之类的问题. 1.Prim算法 ①该算法是构建最小生成树的算法之一.它是

关于最小生成树的Prim算法和Kruskal算法

针对一些城市之间建造公路或者造桥问题,我们需要的是以最小代价完成任务,看了一下别人的代码,思想其实都是差不多,但是感觉不大好理解,比如Kruskal算法中有人写了利用递归实现判断是否形成环,本人愚钝,没有想通...现在就算重新整理一遍,也帮自己梳理一下. 这两段代码已经经过本人调试,能够完美运行,希望对大家有帮助. (1)Prim算法 Prim算法实际上是属于贪心算法吧,每走一步,遍历一次是为了寻找对于当前最需要的点,即最小的邻接边,这点实际上与寻找单源最短路径的Dijkstra算法一样,不过在

转载:最小生成树-Prim算法和Kruskal算法

本文摘自:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html 最小生成树-Prim算法和Kruskal算法 Prim算法 1.概览 普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小.该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克(英语:

最小生成树Prim算法和Kruskal算法

Prim算法(使用visited数组实现) Prim算法求最小生成树的时候和边数无关,和顶点树有关,所以适合求解稠密网的最小生成树. Prim算法的步骤包括: 1. 将一个图分为两部分,一部分归为点集U,一部分归为点集V,U的初始集合为{V1},V的初始集合为{ALL-V1}. 2. 针对U开始找U中各节点的所有关联的边的权值最小的那个,然后将关联的节点Vi加入到U中,并且从V中删除(注意不能形成环). 3. 递归执行步骤2,直到V中的集合为空. 4. U中所有节点构成的树就是最小生成树. 方法

Prim算法和Kruskal算法的正确性证明

今天学习了Prim算法和Kruskal算法,因为书中只给出了算法的实现,而没有给出关于算法正确性的证明,所以尝试着给出了自己的证明.刚才看了一下<算法>一书中的相关章节,使用了切分定理来证明这两个算法的正确性,更加简洁.优雅并且根本.相比之下,我的证明带着许多草莽气息,于此写成博客,只当是记录自己的思考 ------------------------------------------- 说明: 本文仅提供关于两个算法的正确性的证明,不涉及对算法的过程描述和实现细节 本人算法菜鸟一枚,提供的

最小生成树-Prim算法和Kruskal算法

原文链接:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html Prim算法 1.概览 普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小.该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克(英语:Vojtěch Jarník)发现:并在195

[转载]最小生成树-Prim算法和Kruskal算法

转载地址:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html 自己在学,感觉这个讲的很不错,就转载了. Prim算法 1.概览 普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小.该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克(英语:Vo