• 【算法】prim算法(最小生成树)(与Dijkstra算法的比较)


    最小生成树:

      生成树的定义:给定一个无向图,如果它的某个子图中任意两个顶点都互相连通并且是一棵树,那么这棵树就叫做生成树。(Spanning Tree)

      最小生成树的定义:在生成树的基础上,如果边上有权值,那么使得边权和最小的生成树叫做最小生成树。(Minimum Spanning Tree )

    解决生成树有两种常用的算法:Kruskal算法和prim算法。

    这里我们讲的是prim算法求生成树的解法。

    算法思想:

      ans = 0;(表示权值和)  

      1.在无向图的基础上,想象我们有一个点的集合X(初始状态为空)。

      2.在集合X中加入一个初始点x,用这个初始点更新其他点离集合X的距离mincost[ ],标记初始点为使用过(使用过:加入集合X)。

      3.找一个未使用过(集合X外的点)的离集合X最小距离最小的点,找到了这样一个点,将这个点加入集合X,ans += 这个与集合X的距离,

        用这个新加入的点更新其他点离集合X的最小距离mincost[ ],标记新加入的点为使用过,继续执行第3步;找不到这样一个点,则进入第4步。

      4.输出ans。

     mincost[i]  表示点i离集合X的最小距离。(离集合X中所有点中最近的点的距离)

    简单来说就是:

      想象一下,有10张面额不同的毛爷爷在你面前,每次只能拿1张,只能拿5次,你肯定会每次都拿这n张(n<=10)中最大的那张,

           这样拿5次,让总额最大。这个算法也是同样的道理,不断的加入可触及的最近的点,最后权值一定是最小的。

      额,当然10张不同的毛爷爷是不存在的。所以一分都拿不到,还是看代码吧:

    代码:

    #include <bitsstdc++.h>
    using namespace std;
    #define INF 2147483647
    #define MAX_V 1000
    #define MAX_E 2000 
    
    int cost[MAX_V][MAX_V];  // cost[i][j] 表示顶点i到顶点j的权值,不存在时为INF 
    int mincost[MAX_V];   // mincost[i] 表示i点与集合X的最小距离 
    bool used[MAX_V];    //  used[i]表示点i是否在集合中 
    int V;   //顶点数 
    
    //表示从点x产生的最小生成树,这么考虑是因为整个图可能不连通 
    int prim(int x){
        //最初X集合为空,每个点到集合X的最小距离都是INF 
        for(int i = 0;i < V; ++i){
            mincost[i] = INF;
            used[i] = false;
        }
        
        //将点x与集合X的距离置为0,第一次集合X会加入点x 
        mincost[x] = 0;
        
        int res = 0;
        
        while(true){
            int v = -1;
            //找到离集合X最近的点,第一次加入点x 
            for(int u = 0;u < V; ++u){
                if(!used[u] && (v == -1 || mincost[u] < mincost[v])) v = u;
            }
            //如果所有点可达的点都加入集合X中了,就跳出 
            if(v == -1) break;
            used[v] = true;
            res += mincost[v];
    
    
            mincost[v] = 0;         //把点v加入到集合X中,这一步帮助理解,可写可不写 ,因为cost[v][v] = 0 
    
            //用新加入的点v更新其他点离与集合X的最小距离 
            for(int u = 0;u < V; ++u){
                mincost[u] = min(mincost[u] , cost[v][u]);
            }
        }
        return res;
    }
    
    int main(){
    } 

      

    与Dijkstra算法的比较

      Prim算法与Dijkstra算法都是从某个点出发,不断加入最近的点。最终都要把所有可以加的点加完。

      Prim算法是求最小生成树,Dijkstra是求单源最短路径。

    来个Dijkstra求单源最短路径的代码,与Prim算法比较一下:

    #include <bitsstdc++.h>
    using namespace std;
    #define INF 2147483647
    #define MAX_V 1000
    #define MAX_E 2000 
    
    //单源最短路径问题(Dijkstra算法) 
    
    
    int cost[MAX_V][MAX_V];  //cost[u][v]表示e = (u,v)的权值 
    int d[MAX_V];        //顶点s出发的最短距离    //不同处1
    bool used[MAX_V];    //标记使用过的点 
    int V;          //顶点数 
    
    void dijkstra(int s){
        fill(d, d+V, INF);
        fill(used, used + V, INF);
        d[s] = 0;
        
        while(true){
            int v = -1;
            
            //找到一个距离最近的没有使用过的点 
            for(int u = 0;u < V; u++){
                if(!used[u] && (v == -1 || d[u] < d[v])) v = u;
            }
            //如果所有的点都被使用过了,则break
            if(v == -1) break;
            
            //标记当前点被使用过了 
            used[v] = true;
            
            //更新这个找到的距离最小的点所连的点的距离 
            for(int u = 0;u < V; u++){
                d[u] = min(d[u], d[v] + cost[v][u]);  //不同处2
            }
            
        }
    }
    
    
    int main(){
    } 

     

    我们可以看到代码基本上是一样的,只有

    不同处1:Djikstra中用d[i]表示i点离源点的最短距离,Prim中用mincost[i] 表示i点与集合X的距离。

    不同处2:Djikstra中更新d[u] = min( d[u] , d[v] + cost[v][u] ); 用新加入的点更新其他点与源点的最小距离。

         Prim中更新mincost[u] = min(  mincost[u] , cost[v][u]  ); 用新加入的点更新点i与集合X的最小距离。

    我认为只用加几行代码,无论是在prim中加,还是在Dijkstra中加,就既可以求单源最短路径,又可以求最小生成树了。

  • 相关阅读:
    python3初识selenium
    [lucene系列笔记3]用socket把lucene做成一个web服务
    [lucene系列笔记2]在eclipse里初步使用lucene的索引和查询功能
    [lucene系列笔记1]lucene6的安装与配置(Windows系统)
    JAVA SOCKET
    Python3 urlparse
    Windows Socket 编程_ 简单的服务器/客户端程序
    linux软件包管理
    linux用户及权限管理
    docker搭建私有仓库
  • 原文地址:https://www.cnblogs.com/zhangjiuding/p/7725832.html
Copyright © 2020-2023  润新知