Prim算法(求连通图的最小加权数)

Prim算法

涉及到的几个基础知识

  • 生成树: 一个连通图的生成树是它的极小连通子图,在n个顶点的情形下,有n-1条边。生成树是对连通图而言的,是连通图的极小连通子图,包含途中所有顶点,有且仅有n-1条边。非连通图的生成树则组成一个声称森林;若图中有n个顶点,m个连通分量,则生成森林中有n-m条边。

  • 图的遍历: 和树的遍历相似,若从图中某顶点出发,访问遍途中每个顶点,且每个顶点仅访问一次,此过程称为图的遍历。图的遍历算法是求解图的连通性问题、拓扑排序和求关键路径等算法的基础。图的常用遍历顺序有两种:深度优先搜索(DFS)和广度优先搜索(BFS),对每种搜索顺序,访问各顶点的顺序也不是唯一的。

  • 在一个无向连通图G中,其所有顶点和遍历该图经过的所有边所构成的子图G',称作图G的生成树。一个图可以有多个生成树,从不同的顶点除法,采用不同的遍历顺序,遍历时所经过的边也就不同。

  • 最小生成树:在图论中,常常将树定义为一个无回路连通图。对于一个带权的无向连通图,其每个生成树所有边上的权值之和可能不同,我们把所有边上权值之和最小的生成树成为图的最小生成树(MST)。

  • MST性质:MST性质:假设G=(V,E)是一个连通网,U是顶点V的一个非空子集。若(u,v)是一条具有最小权值的边,其中u∈U,v∈V-U,则必存在一棵包含边(u,v)的最小生成树。

Prim算法

  • 基本思想:假设G=(V,E)是连通的,TE是G上最小生成树中边的集合。算法从U={u0}(u0∈V)、TE={}开始。重复执行下列操作:

  • 在所有u∈U,v∈V-U的边(u,v)∈E中找一条权值最小的边(u0,v0)并入集合TE中,同时v0并入U,直到V=U为止。
    此时,TE中必有n-1条边,T=(V,TE)为G的最小生成树。

  • Prim算法的核心:始终保持TE中的边集构成一棵生成树。

  • Prim算法举例:

采用的是顶点数为6的无向连通图:

设集合V={A,B,C,D,E,F},即所有顶点的集合。

集合U为最小生成树的结点。

按照Prim算法:

  1. 将A加入U,此时,U={ A },V-U={ B,C,D,E};
  2. 与A邻接的顶点有B,C,D。(A,B)、(A,C)、(A,D),权值分别为6、1、5,因而选定(A,C)为最小生成树的一条边;
  3. 将上一步选定的在V-U中的顶点C加入U,此时,U={A,C}, V-U={ B,D,E,F};
  4. V-U中与U中顶点组成的边有(A,B)、(A,D)、(C,B)、(C,D)、(C,E)、(C,F),权值分别为6、5、5、5、6、4,因而选定(C,F)为最小生成树的一条边;
  5. 将F加入U中,此时U={ A,C,F} , V-U={ B, D,E};
  6. V-U中与U中顶点组成的边有(A,B)、(A,D)、(C,B)、(C,D)、(C,E)、(F,D)、(F,E),权值分别为6、5、5、5、6、2、6,选定(F,D)为最小生成树的一条边;
  7. 将D加入U中,此时,U={ A,C,F,D}, V-U={ B,E};
  8. V-U中与U中顶点组成的边有(A,B)、(C,B)、(C,E)、(F,E),权值分别为6、5、6、6,选定(C,B)为最小生成树的一条边。
  9. 将B加入U中,此时U= {A,C,F ,D,B }, V -U={E };
  10. V-U中与U中顶点组成的边有(B,E)、(C,E)、(F,E),权值分别为3、6、6,选定(B,E)为最小生成树的一条边。
  11. 将E加入U中,此时U={A ,C,F,D,B,E},完成MST的生成。
其生成过程图示如下:

  1.   
        

          













  2. 下面给出一道题    
  3. 题意:输入n个城镇相互之间的距离,输出将n个城镇连通费用最小的方案中修的最长的路的长度

    这个也是最小生成树的题,只不过要求的不是最小价值,而是最小生成树中的最大权值,只需要加个判断

    比较最小生成树每条边的大小就行          

  4. 代码

    #include<cstdio>    
    #include<algorithm>    
    #include<cstring>    
    using namespace std;    
    #define N 510    
    #define inf 0x3f3f3f3f//无穷大    
    int map[N][N];  //两点间距离   
    int dis[N]; // 到N点的最短路   
    int vis[N];  //标记   
    int n;    
    void prim(int u)    
    {    
        int i,j,k,start,min,max;    
        memset(vis,0,sizeof(vis)); //初始化   
        for(i=1;i<=n;i++)    
            if(i!=u)    
                dis[i]=map[1][i]; //1到i的距离   
        vis[1]=true;  //加入点1,设为起点   
        k=0;    
        min=0;max=0;    
        for(i=1;i<n;i++)        //n-1条边    
        {    
            min=inf; //不能直接连接的设为无穷大   
            for(j=1;j<=n;j++)    
            if(dis[j]<min&&!vis[j])    
            {    
                min=dis[j];    
                k=j;  //加入点   
            }    
            if(min>max)    
            max=min;    
            vis[k]=true;    
            for(j=1;j<=n;j++)    
            if(dis[j]>map[k][j]&&!vis[j])    
            {    
                dis[j]=map[k][j];    
            }    
        }    
        printf("%d\n",max);   
    }     
    int main()    
    {    
        int T;    
        scanf("%d",&T);    
        while(T--)    
        {    
            scanf("%d",&n);    
        memset(map,inf,sizeof(map));//初始化    
        for(int i=1;i<=n;i++)  //两端点   
        for(int j=1;j<=n;j++)    
        {    
            scanf("%d",&map[i][j]);    
        }     
            prim(1);    
        }    
        return 0;    
    }   
  5. #include<stdio.h>/*这是一个模板*/  
    #include<stdlib.h>  
    #define  MAX 10000  
      
      
    int edge_num;          //总边数  
    int vertex_num;        //顶点总数  
    int sum;               //最小生成树的边权之和   
    int s;                 //指定的最小生成树的起点  
    int matrix[100][100];  //定义邻接矩阵  
    bool visited[100];     //定义标记数组  
    int l_cost[100];       //定义边的权值  
    int path[100];         //记录最小生成树的路径  
      
      
    void Prim(int s){  
        int min;           //权值最小  
        int min_index;     //权值最小的下标  
        sum=0;             //初始化权和为0   
        visited[s]=true;   //标记顶点   
        for(int i=0;i<vertex_num;i++){     
            l_cost[i]=matrix[s][i];      //遍历数据,初始化起点s的各邻接边权值   
            path[i]=s;                   //初始化相应从起点到i点的路径   
        }   
        for(int i=1;i<vertex_num;i++){  
            min=MAX;  
            for(int j=0;j<vertex_num;j++){   //找出权值最小   
                if(visited[j]==false && l_cost[j]<min){  
                    min=l_cost[j];         //min=最小边权值   
                    min_index=j;           //记录相应的边终点j   
                }  
            }  
            visited[min_index]=true;  //标记找出的结点  
            sum+=l_cost[min_index];   //更新生成树的权和,加上新找到的结点的边权   
            for(int j=0;j<vertex_num;j++){  //利用找到的最小下标更新l_cost数组   
                if(visited[j]==false && matrix[min_index][j]<l_cost[j]){  
                    l_cost[j]=matrix[min_index][j];  
                    path[j]=min_index;  
                }  
            }   
        }  
    }   
      
      
    int main(){  
        int u,v,w;  
        printf("请输入图的顶点数目(不大于100):");  
        scanf("%d",&vertex_num);  
        printf("请输入边数:");  
        scanf("%d",&edge_num);  
        for(int i=0;i<vertex_num;i++){  
            for(int j=0;j<vertex_num;j++){  
                matrix[i][j]=MAX;    //初始化matrix数组(邻接矩阵存储)   
            }  
        }  
        printf("请输入边的信息(起点,终点,边长):\n");  
        for(int i=0;i<edge_num;i++){  //存入各边权信息进邻接矩阵   
            scanf("%d%d%d",&u,&v,&w);  
             matrix[u][v]=w;   
             matrix[v][u]=w;  
         }  
        printf("请输入起点(<%d,&vertex_num):");  
        scanf("%d",&s);  
        Prim(s);  
        printf("最小生成树的边权和为:%d\n",sum);  
        printf("最小生成树的路径为:\n");  
        for(int i=0;i<vertex_num;i++){  
            if(i!=s){  
                printf("%d---%d\n",i,path[i]);  
            }  
        }  
         return 0;  
    }

转载于:https://www.cnblogs.com/acer1238/p/9165510.html

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值