最小生成树–prim算法

首先说明,Dijkstra算法prim算法实际上是相同的思路,只不过是数组d[]的含义不同

详见 算法笔记 P404
prim算法的基本思想是对图G设置集合S(就是个巨型防护罩),用来存放已经被访问的顶点(就是被攻占的城市),然后执行n次下面的两个步骤:

1.每次从集合V-S(就是未被攻占的城市)中选择与集合S(巨型防护罩)最近的一个顶点(记为u),访问(即攻占)u并将其加入集合S(巨型防护罩)的最短距离。

2.令顶点u作为集合S与集合V-S连接的接口(就是把当前攻占的城市作为巨型防护罩与外界的接口,衍生的触手,刚入会就要做贡献),优化从u能到达的未访问顶点v(未攻占城市)与集合S(巨型防护罩)的最短距离。

prim算法的具体实现:
prim算法需要实现两个关键的概念:
集合S的实现
顶点Vi(0<=i<=n-1)与集合S(巨型防护罩)的最短距离

1.集合S的实现方法和Dijkstra中相同,即使用一个bool型数组vis[]表示顶点是否已被访问。其中vis[i]==true表示顶点Vi 已被访问,vis[i]== false 则表示顶点Vi未被访问。
2.令int型数组d[]来存放顶点Vi与集合S(巨型防护罩)的最短距离。初始时除了起点s的d[s]赋为0,其余顶点都赋为一个很大的数来表示INF,就是不可达。

对于最小生成树问题,如果仅是求最小边权之和,在prim算法中可以随意指定一个顶点为初始点。
//prim算法伪代码 模板

//G为图,一般设为全局变量。数组d 为顶点与集合S 的最短距离


Prim( G,d[]  ) 
{
    初始化;
    for(循环n次) 
    {
        u = 是d[u]最小的还未被访问的顶点的标号;
        记u已被访问;

        for(从u出发能到达的所有顶点v) 
        {
            if( v未被访问 && 以u为中介点使得v与集合S的最短距离d[v]更优  )
            {
                将G[u][v]赋值给v与集合S的最短距离d[v]; 


            }



        }


    }





}





//prim算法 完整程序模板

//最大顶点数 
const int MAXV=1000;
//设置一个无穷大
const int  INF=0x3fffffff;


//邻接矩阵版


int n,G[MAXV][MAXV];
//顶点与集合S的最短距离
int d[MAXV];
//标记数组
bool vis[MAXV]={
  false};

//默认0号为初始点,函数返回最小生成树的边权之和

//fill函数将整个d数组赋为INF 
fill(d,d+MAXV,INF ); 


//只有0号顶点到集合S的距离为0,其余全为INF 
d[0]=0; 

//这里不一样!!!存放最小生成树的边权之和 
int ans=0;

//循环n次 
for(int i=0;i<n;i++)
{
    //u使d[u]最小,MIN存放最小的D[u] 
    int u=-1,MIN=INF;

    for(int j=0;j<n;j++) 
    {
        if( vis[j]==false  &&  d[j]<MIN )
        {

            u=j;
            MIN=d[j];


        }

    }


    //找不到小于INF的d[u],则剩下的顶点和集合S不连通 
    if(u==-1)
    {
        return -1;

    }


    //标记u已被访问 
    vis[u]=true;

    //这里不一样!!!将与集合S距离最小的边加入最小生成树 
    ans+=d[u];

    for(int v=0;v<n;v++)
    {
        //v未访问 && u能到达v && 以u为中介点可以使v离集合S更近 
        if( vis[v] == false && G[u][v]!=INF  
            && G[u][v]<d[v]  )
        {
            //将G[u][v] 赋值为 d[v] 
            d[v]=G[u][v];

        }

    }


    //返回最小生成树的边权之和 
    return ans;


}













//邻接表版


struct Node{

    //v为边的目标顶点,dis为边权 
    int v,dis;


}; 


//图G,adj[u]存放从顶点u出发可以到达的所有顶点 
vector<Node> adj[MAXV];


//n为顶点数,图G使用邻接表实现,MAXV为最大顶点数 
int n;

//这里不一样!!!顶点与集合S的最短距离 
int d[MAXV];

bool vis[MAXV]={
  false};


//默认0号为初始点,函数返回最小生成树的边权之和 
int prim()
{
    //fill函数将整个d数组赋为INF 
    fill( d,d+MAXV,INF );

    //只有0号顶点到集合S的距离为0,其余全为INF 
    d[0]=0;

    //存放最小生成树边权之和

    for(int i=0;i<n;i++) 
    {

        int u=-1,MIN=INF;

        for(int j=0;j<n;j++)
        {
            if( vis[j]==false && d[j]<MIN )
            {
                u=j;
                MIN=d[j];

            }

        }


        if(u==-1)
        {

            return -1;


        }

        vis[u]=true;

        //这里不一样!!!将与集合S距离最小的边加入最小生成树 
        ans+=d[u];

        //只有下面 的这个for循环和邻接矩阵的写法不同

        for(int j=0;j<adj[u].size();j++) 
        {
            //通过邻接表直接获得u能到达的顶点v 
            int v=adj[u][j].v;

            if( vis[v]==false && adj[u][j].dis<d[v] )
            {
                d[v]=adj[u][j].dis;

            }



        }





    }


    //返回最小生成树的边权之和 
    return ans;


}