最小生成树问题是实际生产生活中十分重要的一类问题。假设需要在n个城市之间建立通信联络网,则连通n个城市只需要n-1条线路。这时,自然需要考虑这样一个问题,即如何在最节省经费的前提下建立这个通信网。

可以用连通网来表示n个城市以及n个城市之间可能设置的通信线路,其中网的顶点表示城市,边表示两个城市之间的线路,赋于边的权值表示相应的代价。对于n个顶点的连通网可以建立许多不同的生成树,每一棵生成树都可以是一个通信网。现在,需要选择一棵生成树,使总的耗费最小。这个问题就是构造连通网的最小代价生成树,简称最小生成树。一棵生成树的代价就是树上各边的代价之和。

那么如何构建满足以上条件的生成树?这篇日志先介绍其中一种常用的普里姆(Prim)算法。Prim算法构建最小生成树,简单来说就是在图中,从某一顶点出发,逐步构建,让一棵小树逐渐长大。用一个例子来说明更清晰点吧!首先看下面一张无向网图:

数据结构之最小生成树(Prim算法)

要构造这张图的最小生成树,首先,假设我们从V0顶点开始出发,也就是以V0为根结点开始建树,接着往外扩展,从与V0顶点相邻的顶点中找出权值最小的顶点,可以看到是V6,所以把V6和V0连接起来。

数据结构之最小生成树(Prim算法)

也就是把V6收录进了这棵最小生成树中了。接着继续,从当前树中顶点的邻接点中(也就是V0和V6的邻接点中,找出权值最小的顶点)。可以看到是V1,所以把V1也接入最小生成树中。

数据结构之最小生成树(Prim算法)

然后继续,到V2结点,也接入最小生成树中

数据结构之最小生成树(Prim算法)

因为不能形成回路,所以V2和V6之间不能连接(虽然权值最小,等于3)。V0和V1之间也不能连接。所以只能V6和V4连接

数据结构之最小生成树(Prim算法)

最后V4和V5连接,V4再和V6连接,就完成了这棵最小生成树的构建了。

数据结构之最小生成树(Prim算法)

最后就有

 数据结构之最小生成树(Prim算法)

算法实现

数据结构之最小生成树(Prim算法)

代码实现

void  MiniSpanTree_Prim(MGraph   G)
{
    int   min,i,j,k;
    int   adjvex[MAXVEX]; //保存相关顶点下标
    int    lowcost[MAXVEX];//保存相关顶点间边的权值
    lowcost[0] = 0;  //初始化第一个权值为0,即v0加入生成树
       //lowcost的值为0,在这里就是此下标的顶点已加入生成树
     adjvex[0]=0; //初始化第一个顶点下标为0
     for(i=1;i<G.numVertexes;i++){
     {
          lowcost[i]=G.arc[0][i];   //将v0顶点与之有边的权值存入数组。
          adjvex[i]=0; //初始化都为v0的下标
     }
       for(i=1;i<G.numVertexes;i++)
       {

               min = INFINITY;   //初始化最小权值为∞
              j=1;k=0;
               while(j<G.numVertexes)
               {
                     //循环全部顶点
                    if(lowcost[j]!=0 &&lowcost[j]<min)
                    {
                          min =lowcost[i];  //让当前权值成为最小值
                          k=j;  //将当前最小值的下标存入k
                     }
                     j++;
                }
                //printf(“%d,%d”,adjvex[k],k); //打印当前顶点边中权值最小的边
                cout<<adjvex[k]<<"-->"<<k<<endl;
                lowcost[k] = 0; //将当前顶点权值 设置成0表示此顶点已完成任务
                for(j=1;j<G.numVertexes;j++)
               {
                     if(lowcost[j]!=0  && G.arc[k][j]<lowcost[j]){
                        lowcost[j] =G.arc[k][j];
                        adjvex[j] = k;}
               }
       }
}

上题

输入

输入的第一行包含一个正整数n,表示图中共有n个顶点。其中n不超过50。

以后的n行中每行有n个用空格隔开的整数,对于第i行的第j个整数,如果不为0,则表示第i个顶点和第j个顶点有直接连接且代价为相应的值,0表示没有直接连接。当i和j相等的时候,保证对应的整数为0。

输入保证邻接矩阵为对称矩阵,即输入的图一定是无向图,且保证图中只有一个连通分量。

输出

只有一个整数,即最小生成树的总代价。请注意行尾输出换行。

样例输入

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

样例输出

6

代码展示:
 

#include<iostream>
#define max 50
using namespace std;
int n,num=0;
int a[max][max];
void minitree(int a[max][max],int n){
    int   min,i,j,k;
    int   adjvex[max]; //保存相关顶点下标
    int    lowcost[max];//保存相关顶点间边的权值
    lowcost[0]=0;
    adjvex[0]=0;
    for(i=1;i<n;i++){
      lowcost[i]=a[0][i];//将和v0有关的点存入权值数组中
      adjvex[i]=0;//初始化都是v0
    }
    for(i=1;i<n;i++){
      min=1000;
      j=1,k=0;
      while(j<n){
      if(lowcost[j]!=0&&lowcost[j]<min){
        min=lowcost[j];
        k=min;
      }
      j++;
    }
    num+=min;
    lowcost[k]=0;//表明k进入最小生成堆。
    for(j=0;j<n;j++){
      //在这个更新的过程中就是寻找最小的过程
      if(lowcost[j]!=0&&a[k][j]<lowcost[i]){
        lowcost[j]=a[k][j];
        adjvex[j]=k;
      }
    }
    }
}
int main(int argc, char const *argv[]) {
  cin>>n;
  for(int i=0;i<n;i++){
    for(int j=0;j<n;j++){
      cin>>a[i][j];
    }
  }
  //建立邻接矩阵完成
  minitree(a,n);
  cout<<num<<endl;
  return 0;
}

 

相关文章: