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

Posted lewin671

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最小生成树-Prim算法和Kruskal算法相关的知识,希望对你有一定的参考价值。

 

  在解决这个问题之前,我觉得有必要先解释一下什么叫做生成树,什么叫做最小生成树。给定一个图,如果它的某个子图中任意两个顶点都互相联通并且是一棵树,那么这棵树就叫做生成树。如果边上有权值,那么使得权值和最小的树叫做最小生成树。

  安全边:当一条边(u,v)加入T时,必须保证T∪{(u,v)}仍是MST的子集,我们将这样的边称为T的安全边。

  求MST(minimum spanning tree)的一般算法可描述为:针对图G,从空树T开始,往集合T中逐条选择并加入n-1条安全边(u,v),最终生成一棵含n-1条边的MST。求解最小生成树的方法主要有以下两种:

  一、Prim算法:

  贪心的将MST和其他顶点相连的最小权值的边加入MST,重复操作,直到MST中有n条边。如果我们要高效地使用Prim算法,我们可以使用最小堆。下面看一下这个算法的图解:

技术分享图片技术分享图片技术分享图片

技术分享图片

技术分享图片

 

二、kruskal算法

kuskal的贪心策略更加容易理解:每次都选取最小权值的安全边,然后加入MST,直到MST中有n-1条边。当然,高效的查找需要用到并查集,比如下面的第二道例题,而且也需要使用最小堆。下面看kruskal的图解:

技术分享图片技术分享图片技术分享图片

 

 

三、经典题目:

1.HDU 1233

还是畅通工程

描述

某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。 

Input

测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。 
当N为0时,输入结束,该用例不被处理。

 
Output

对每个测试用例,在1行里输出最小的公路总长度。 


Sample Input

3
1 2 1
1 3 2
2 3 4
4
1 2 1
1 3 4
1 4 1
2 3 3
2 4 2
3 4 5
0

Sample Output

3
5

技术分享图片
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=105;
const int INF=0x3fffffff;
int graph[maxn][maxn];
int n;
int Prim()
{
    int min_cost[maxn];
    bool used[maxn];
    for(int i=0;i<maxn;i++)
    {
        min_cost[i]=INF;
        used[i]=false;
    }
    min_cost[1]=0;
    int res=0;

    while(1)
    {
        int v=-1;
        for(int u=1;u<=n;u++)
        {
            if(!used[u]&&(v==-1||min_cost[u]<min_cost[v])) v=u;
        }

        if(v==-1)
            break;

        used[v]=true;
        res+=min_cost[v];

        for(int u=1;u<=n;u++)
        {
            min_cost[u]=min(min_cost[u],graph[v][u]);
        }
    }
    return res;
}

int main()
{
    while(~scanf("%d",&n)&&n)
    {
        for(int i=0;i<=n;i++)
            for(int j=0;j<maxn;j++)
                graph[i][j]=INF;
        int vertex1,vertex2,weight;
        for(int i=1;i<=n*(n-1)/2;i++)
        {
            scanf("%d%d%d",&vertex1,&vertex2,&weight);
            graph[vertex1][vertex2]=weight;
            graph[vertex2][vertex1]=weight;
        }
        printf("%d\\n",Prim());
    }
}
View Code

 

2.Constructing Roads  (HDU 1102)

There are N villages, which are numbered from 1 to N, and you should build some roads such that every two villages can connect to each other. We say two village A and B are connected, if and only if there is a road between A and B, or there exists a village C such that there is a road between A and C, and C and B are connected. 

We know that there are already some roads between some villages and your job is the build some roads such that all the villages are connect and the length of all the roads built is minimum. 

InputThe first line is an integer N (3 <= N <= 100), which is the number of villages. Then come N lines, the i-th of which contains N integers, and the j-th of these N integers is the distance (the distance should be an integer within [1, 1000]) between village i and village j. 

Then there is an integer Q (0 <= Q <= N * (N + 1) / 2). Then come Q lines, each line contains two integers a and b (1 <= a < b <= N), which means the road between village a and village b has been built. 
OutputYou should output a line contains an integer, which is the length of all the roads to be built such that all the villages are connected, and this value is minimum. 
Sample Input

3
0 990 692
990 0 179
692 179 0
1
1 2

Sample Output

179

技术分享图片
#include<set>
#include<cstdio>
using namespace std;

struct Node
{
    int x,y,cost;
    Node() {}
    Node(int xx,int yy,int cc):x(xx),y(yy),cost(cc){}
    friend bool operator<(Node a,Node b);
};

const int maxn=10000;
int fa[maxn];

void init()
{
    for(int i=0;i<maxn;i++) fa[i]=i;
}

int GetRoot(int x)
{
    if(x!=fa[x])
    {
        return fa[x]=GetRoot(fa[x]);
    }

    return fa[x];
}

void Unite(int x,int y)
{
    int fx=GetRoot(x);
    int fy=GetRoot(y);
    fa[fx]=fy;
}

int main()
{
    int n;
    multiset<Node> s;
    while(~scanf("%d",&n))
    {
        init();
        s.clear();
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                int num;
                scanf("%d",&num);
                if(i!=j)
                {
                    s.insert(Node(i,j,num));
                }
            }
        }
        int q,ans=0,counter=0;  //counter表示已经连接的边数
         scanf("%d",&q);
        while(q--)
        {
            int xx,yy;
            scanf("%d%d",&xx,&yy);
            if(GetRoot(xx)!=GetRoot(yy))
                counter++;
            Unite(xx,yy);
        }
        multiset<Node>::iterator it=s.begin();
        while(counter<n-1)
        {
            Node node=(*it);
            int rx=GetRoot(node.x);
            int ry=GetRoot(node.y);
            if(rx!=ry)
            {
                Unite(rx,ry);
                ans+=it->cost;
                counter++;
            }
            it++;
        }
        printf("%d\\n",ans);
    }
}

bool operator<(Node a,Node b)
{
    return a.cost<b.cost;
}
View Code

 














以上是关于最小生成树-Prim算法和Kruskal算法的主要内容,如果未能解决你的问题,请参考以下文章

图解:如何实现最小生成树(Prim算法与Kruskal算法)

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

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

最小生成树及Prim算法及Kruskal算法的代码实现

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

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