zoukankan      html  css  js  c++  java
  • 最小生成树的两种算法

    最小生成树prim算法实现

    所谓生成树,就是n个点之间连成n-1条边的图形。而最小生成树,就是权值(两点间直线的值)之和的最小值。

       

      

      首先,要用二维数组记录点和权值。如上图所示无向图:

    int map[7][7];
           map[1][2]=map[2][1]=4;
           map[1][3]=map[3][1]=2;
           ......

          然后再求最小生成树。具体方法是:

    1.先选取一个点作起始点,然后选择它邻近的权值最小的点(如果有多个与其相连的相同最小权值的点,随便选取一个)。如1作为起点。

    visited[1]=1;

    pos=1;

    //用low[]数组不断刷新最小权值,low[i](0<i<=点数)的值为:i点到邻近点(未被标记)的最小距离。

    low[1]=0;  //起始点i到邻近点的最小距离为0

    low[2]=map[pos][2]=4;

    low[3]=map[pos][3]=2;

    low[4]==map[pos][4]=3;

    low[5]=map[pos][5]=MaxInt;  //无法直达

    low[6]=map[pos][6]=MaxInt;

     

      2.再在伸延的点找与它邻近的两者权值最小的点。

    //low[]以3作当前位置进行更新

    visited[3]=1;

    pos=3;

    low[1]=0;   //已标记,不更新

    low[2]=map[1][2]=4;  //比5小,不更新

    low[3]=2;  //已标记,不更新

    low[4]=map[1][4]=3;   //比1大,更新后为:low[4]=map[3][4]=1;

    low[5]=map[1][5]=MaxInt;//无法直达,不更新

    low[6]=map[1][6]=MaxInt;//比2大,更新后为:low[6]=map[3][6]=2;

     

        3.如此类推...

     
     
         当所有点都连同后,结果最生成树如上图所示。

         所有权值相加就是最小生成树,其值为2+1+2+4+3=12。

         至于具体代码如何实现,现在结合POJ1258例题解释。代码如下:

     1 //poj-1258 
     2 #include <stdio.h> 
     3 #include <string.h> 
     4 #define MaxInt 0x3f3f3f3f 
     5 #define N 110 
     6 //创建map二维数组储存图表,low数组记录每2个点间最小权值,visited数组标记某点是否已访问 
     7 int map[N][N],low[N],visited[N]; 
     8 int n;   
     9 int prim() 
    10 { 
    11     int i,j,pos,min,result=0; 
    12     memset(visited,0,sizeof(visited)); 
    13     visited[1]=1;pos=1; //从某点开始,分别标记和记录该点 
    14     for(i=1;i<=n;i++)   //第一次给low数组赋值 
    15         if(i!=pos) low[i]=map[pos][i]; 
    16     for(i=1;i<n;i++)    //再运行n-1次 
    17     {         
    18         min=MaxInt; //找出最小权值并记录位置 
    19         for(j=1;j<=n;j++) 
    20          if(visited[j]==0&&min>low[j]) 
    21          { 
    22              min=low[j];pos=j; 
    23          } 
    24         result+=min;    //最小权值累加 
    25         visited[pos]=1;     //标记该点 
    26         for(j=1;j<=n;j++)   //更新权值 
    27         if(visited[j]==0&&low[j]>map[pos][j]) 
    28             low[j]=map[pos][j]; 
    29     } 
    30     return result; 
    31 } 
    32   
    33 int main() 
    34 { 
    35     int i,v,j,ans; 
    36     while(scanf("%d",&n)!=EOF) 
    37     { 
    38         memset(map,MaxInt,sizeof(map)); //所有权值初始化为最大 
    39         for(i=1;i<=n;i++) 
    40             for(j=1;j<=n;j++) 
    41             { 
    42                 scanf("%d",&v); 
    43                 map[i][j]=map[i][j]=v; 
    44             } 
    45             ans=prim(); 
    46             printf("%d
    ",ans); 
    47     } 
    48     return 0; 
    49 }

    http://acm.nyist.net/JudgeOnline/problem.php?pid=38

    nyoj—38代码:

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 using namespace std;
     5 
     6 int map[550][550],vis[550],low[550];
     7 int n;
     8 int prim()
     9 {
    10     int i,j,pos,min,sum=0;
    11     memset(low,0,sizeof(low));
    12     memset(vis,0,sizeof(vis));
    13     vis[1]=1;pos=1;
    14     for(i=1;i<=n;i++)
    15     if(i!=pos)
    16     low[i]=map[pos][i];
    17     for(i=1;i<n;i++)
    18     {
    19         min=200;
    20         for(j=1;j<=n;j++)
    21         {
    22             if(!vis[j]&&low[j]<min)
    23             {
    24                 min=low[j];
    25                 pos=j;
    26             }
    27         }
    28         sum+=min;
    29         vis[pos]=1;
    30         for(j=1;j<=n;j++)
    31         {
    32             if(vis[j]==0&&low[j]>map[pos][j])
    33             low[j]=map[pos][j];
    34         }
    35     }
    36     return sum;
    37 }
    38 int main()
    39 {
    40     int T;
    41     scanf("%d",&T);
    42     while(T--)
    43     {
    44         int i,j,m,v,e,c;
    45         int s[550];
    46         memset(map,0,sizeof(map));
    47         memset(s,0,sizeof(s));
    48         scanf("%d %d",&n,&m);
    49         //if(n==0&&m==0)
    50         //{
    51         //    printf("0
    ");
    52         //    continue;
    53         //}
    54         for(i=0;i<=n;i++)
    55         for(j=0;j<=n;j++)
    56         map[i][j]=map[j][i]=200;
    57         for(i=0;i<m;i++)
    58         {
    59             scanf("%d %d %d",&v,&e,&c);
    60             map[v][e]=map[e][v]=c;
    61         }
    62         for(i=0;i<n;i++)
    63         scanf("%d",&s[i]);
    64         sort(s,s+n); 
    65         printf("%d
    ",prim()+s[0]);
    66     }
    67     return 0;
    68 }
    69 //prim算法 
    70 //初始化时没有把 vis数组清零导致 wa 
    View Code

    最小生成树Kruskal算法+并查集实现

     

    今天刚掌握Kruskal算法,写下随笔。

    对于稀疏图来说,用Kruskal写最小生成树效率更好,加上并查集,可对其进行优化。

    Kruskal算法的步骤:

    1.对所有边进行从小到大的排序。

    2.每次选一条边(最小的边),如果如果形成环,就不加入(u,v)中,否则加入。那么加入的(u,v)一定是最佳的。

    并查集:
    我们可以把每个连通分量看成一个集合,该集合包含了连通分量的所有点。而具体的连通方式无关紧要,好比集合中的元素没有先后顺序之分,只有“属于”与“不属于”的区别。图的所有连通分量可以用若干个不相交集合来表示。

    而并查集的精妙之处在于用数来表示集合。如果把x的父结点保存在p[x]中(如果没有父亲,p[x]=x),则不难写出结点x所在树的递归程序:

    find(int x) {return p[x]==x?x:p[x]=find(p[x]);}

    意思是,如果p[x]=x,说明x本身就是树根,因此返回x;否则返回x的父亲p[x]所在树的根结点。

    既然每棵树表示的只是一个集合,因此树的形态是无关紧要的,并不需要在“查找”操作之后保持树的形态不变,只要顺便把遍历过的结点都改成树根的儿子,下次查找就会快很多了。如下图所示:

     
    设第i条边的端点序号和权值分别保存在u[i],v[i],w[i]中,而排序后第i小的边保存在r[i]中。(间接排序是指排序的关键字是对象的代号,而不是对象本身。)
    结合hdoj1863代码如下:
     1 //hdu_1863
     2 #include <stdio.h> 
     3 #include <stdlib.h> 
     4 #include <algorithm> 
     5 #define N 150 
     6 using namespace std; 
     7 int m,n,u[N],v[N],w[N],p[N],r[N]; 
     8 int cmp(const int i,const int j) {return w[i]<w[j];} 
     9 int find(int x) {return p[x]==x?x:p[x]=find(p[x]);} 
    10 int kruskal() 
    11 { 
    12     int cou=0,x,y,i,ans=0; 
    13     for(i=0;i<n;i++) p[i]=i; 
    14     for(i=0;i<m;i++) r[i]=i; 
    15     sort(r,r+m,cmp); 
    16     for(i=0;i<m;i++) 
    17     { 
    18         int e=r[i];x=find(u[e]);y=find(v[e]); 
    19         if(x!=y) {ans += w[e];p[x]=y;cou++;} 
    20     } 
    21     if(cou<n-1) ans=0; 
    22     return ans; 
    23 } 
    24   
    25 int main() 
    26 { 
    27     int i,ans; 
    28     while(scanf("%d%d",&m,&n)!=EOF&&m) 
    29     { 
    30         for(i=0;i<m;i++) 
    31         { 
    32             scanf("%d%d%d",&u[i],&v[i],&w[i]); 
    33         } 
    34         ans=kruskal(); 
    35         if(ans) printf("%d
    ",ans); 
    36         else printf("?
    ",ans); 
    37     } 
    38     return 0; 
    39 }
    View Code

    我的hdu1863代码:

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1863

     1 #include <cstdio>
     2 #include <iostream>
     3 #include <cstring>
     4 //#include <algotithm>
     5 #include <stdlib.h>
     6 using namespace std;
     7 typedef struct IN
     8 {
     9     int a;
    10     int b;
    11     int c;
    12 }IN;
    13 IN s[5000];
    14 int N,M;
    15 int pre[110];
    16 int cmp(const void *a,const void *b)
    17 {
    18     return (*(IN *)a).c - (*(IN *)b).c;
    19 }
    20 int find(int x)
    21 {
    22     int i,r,t;
    23     r=x;
    24     while(r!=pre[r])
    25     r=pre[r];
    26     while(x!=r)
    27     {
    28         i=pre[x];
    29         pre[x]=r;
    30         x=i;
    31     }
    32     return r;
    33 }
    34 int kruskal()
    35 {
    36     int i,j,pa,pb,num=0,sum=0;
    37     for(i=0;i<=M;i++)
    38     pre[i]=i;
    39     for(i=0;i<N;i++)
    40     {
    41         pa=find(s[i].a);
    42         pb=find(s[i].b);
    43         if(pa!=pb)
    44         {
    45             pre[pa]=pb;
    46             sum+=s[i].c;
    47             num++;
    48         }
    49     }
    50     if(num==M-1)
    51     return sum;
    52     else
    53     return 0;
    54 }
    55 int main()
    56 {
    57     while(scanf("%d %d",&N,&M),N)
    58     {
    59         int i,j,t;
    60         memset(s,0,sizeof(s));
    61         for(i=0;i<N;i++)
    62         scanf("%d %d %d",&s[i].a,&s[i].b,&s[i].c);
    63         qsort(s,N,sizeof(s[0]),cmp);
    64         //for(i=0;i<N;i++)
    65         //printf("%d
    ",s[i].c);
    66         t=kruskal();
    67         if(t)
    68         printf("%d
    ",t);
    69         else
    70         printf("?
    ");
    71     }
    72     return 0;
    73 }
  • 相关阅读:
    二叉树--转换二叉树(leetcode 108,leetcode 109)
    二叉树--层序遍历(leetcode 102
    二叉树--对称二叉树(leetcode 101
    数据库事务隔离
    二叉树--后序遍历的递归和非递归(leetcode 145
    二叉树--中序遍历的递归和非递归(leetcode 94
    二叉树--先序遍历的递归和非递归(leetcode 144
    链表--排序链表(leetcode 148
    接口的调用
    查锁表以及杀进程
  • 原文地址:https://www.cnblogs.com/xl1027515989/p/3597091.html
Copyright © 2011-2022 走看看