zoukankan      html  css  js  c++  java
  • 最小生成树

    1. 无向图与开放树

      在讲最小生成树之前,先来说一说开放树。连通而无环路的无向图称做开放树。如果指定开放树中某一顶点为根,并且把每一条边看成是背离根的,则一棵开放树变成一棵树。

    开放树有2个性质:

    • 具有n>=1个顶点的开放树包含n-1条边
    • 如果在开放树中任意增加一条边,将构成一个环路

    如下列两个图:

    image_1be1se15l19kkj7f15k6plugod9.png-7.8kB

    2. 最小生成树

    只针对无向图

      假设E中的每条边都有权重,为c(u,v),也叫做边长。图G的一棵生成树是连接V中所有顶点的一棵开放树。将生成树中所有边长的总和称为生成树的价。使这个价最小的生成树称为图的最小生成树

      设G=(V,E)是一个连通图,在E上定义一个权函数C,且{(V1,T1),(V2,T2),...,(Vk,Tk)}是G的任意生成森林,令

        image_1be1srah117bj110t91lc8v17q7m.png-2.5kB

      其中,|T| <= |E|,又假设e=(v,w)是E-T中的一条边,其权值C[v][w]最小,而且v在V1中,w不在V1中,则图G有一棵包含T和e的生成树,其价不大于包含T的任何生成树的价。

    以上的性质下,两种算法应运而生:Prim算法和Kruskal算法

    • Prim算法
        /*输入为加权无向图G=(V,E),其中V={1,2,3,...,n}
         要点:引进集合U和T,U准备放顶点,T准备放边。初值为U={1},T为空,选择最小权的边(u,v),使u在U中,v在V-U中,将v加入U,(u,v)加入T。重复这个过程 */
         
         
         void Prim(costtype C[n+1][n+1])
         {
            /*CLOSECT表示U中的顶点,(i,CLOSECT[i])具有最小的权,而LOWCOST[i]表示该边的权,其中i不在U中  */
            
            costtype LOWCOST[n+1];
            int CLOSEST[n+1];
            int i,j,k;
            costtype min;
            for(i=2;i <= n;i++)
            {
                //初始化
                LOWCOST[i] = C[1][i];
                CLOSEST[i] = 1;
            }
            
            for(i=2;i=n;i++)
            {
                min = LOWCOST[i];
                k=i;
                //找出最小权的边
                for(j=2;j<=n;j++) {
                    if(LOWCOST[j] < min) {
                        min = LOWCOST[j];
                        k = j;
                    }
                cout<<"("<<k<<","<<CLOSECT[k] << ")"<<endl;
                LOWCOST[k] = infinity;  /* k加入U */
                
                
                //对新加入的k的基础上,更新LOWCOST和CLOSECT
                for(j = 2;j<=n;j++) {
                    if(C[k][j] < LOWCOST[j] && LOWCOST[j] != infinity) {
                        LOWCOST[j] = C[k][j];
                        CLOSECT[j] = k;
                    }
                }
            }
         }
    
    • Kruskal算法
        void Kruskal(V,T)
        {
            T = V;
            ncomp = n;
            while(ncomp > 1) {
                从E中取出并删除权最小的边(v,u)
                if(v和u属于T中不同的连通分量) {
                    T= T + (v,u);
                    ncomp --;
                }
            }
        }
    

    Prim算法复杂度为O(n2),而Kruskal算法复杂度为O(ne),故Prim算法适用于点较少的情况

    3. 最小树形图

    只针对有向图

      首先为除根之外的每个点选定一条入边,这条入边一定要是所有入边中最小的。现在所有的最小入边都选择出来了,如果这个入边集不存在有向环的话,我们可以证明这个集合就是该图的最小树形图。这个证明并不是很难。如果存在有向环的话,我们就要将这个有向环缩成一个人工顶点,同时改变图中边的权。假设某点u在该环上,并设这个环中指向u的边权是in[u],那么对于每条从u出发的边(u, i, w),w为权,在新图中连接(new, i, w)的边,其中new为新加的人工顶点; 对于每条进入u的边(i, u, w),在新图中建立边(i, new, w-in[u])的边。

    image_1be20ei6d1dh91r02rb6r9fa5p13.png-250.9kB

        int ZLEdmonds(int n,int map[maxn][maxn])  
        {  
            bool visited[maxn],flag[[maxn];  
            int pre[maxn];  
            int sum=0,i,j,k;  
            for( i=0; i<n; i++){  
                 flag[i]=false;  
                 map[i][i]=INF;  
            }  
            pre[0]=0;  
            while( true){  
                   //求最短弧集合E0。  
                   for( i=1; i<n; i++){  
                        if( flag[i]) continue;  
                        pre[i]=i;  
                        for( j=0; j<n; j++){   //pre[i]保存终点为i的最短弧的起点。  
                             if( !flag[j]&&map[j][i]<map[pre[i]][i])  
                                 pre[i]=j;      
                        }  
                        if( pre[i]==i) return -1;  
                   }   
                   //检查E0  
                   for( i=1; i<n; i++){  
                        if( flag[i]) continue;  
                        for( j=0; j<n; j++)  
                             visited[j]=false;  
                        visited[0]=true;  
                          
                        j=i;  
                        do{  
                            visited[j]=true;  
                            j=pre[j];  
                        }while( !visited[j]);   
                        if( !j) continue; //没有找到环。  
                           
                        i=j;//将整个环的权值保存,累计入原图的最小树形图  
                        do{  
                            sum+=map[pre[j]][j];  
                            j=pre[j];  
                        }while( j!=i);  
                          
                        j=i;//对于环上的点有关的边,修改边权  
                        do{  
                            for( k=0; k<n; k++){  
                                 if( !flag[k]&&map[k][j]&&map[k][j]<INF&&k!=pre[j])  
                                     map[k][j]-=map[pre[j]][j];  
                            }  
                            j=pre[j];  
                        }while( j!=i);  
                          
                        //缩点,将整个环缩成i号点,所有环上的点有关的边转移到点i  
                        for( j=0; j<n; j++){  
                             if( j==i) continue;  
                             for( k=pre[i]; k!=i; k++){  
                                    
                                  if( map[k][j]<map[i][j])  
                                      map[i][j]=map[k][j];  
                                  if( map[j][k]<map[j][i])  
                                      map[j][i]=map[j][k];   
                             }  
                        }  
                        //标记环上其他的点为被缩掉  下次再找Ei时不参与   
                        for( j=pre[i]; j!=i; j=pre[j])  
                             falg[j]=true;  
                        //当前环缩点结束,形成新的图G',跳出继续求G'的最小树形图 ,累计入sum。   
                   }   
                   if( i==n){  
                       for( i=0; i<n; i++)  
                            if( !flag[i])  
                                sum+=map[pre[i]][i];  
                       break;  
                   }  
            }          
                 return sum;          
        }  
    
  • 相关阅读:
    明确目标
    适应环境
    解决问题的方式
    超市收银系统设计
    功能开发流程
    JS代码和OC代码的相互调用
    app上架流程的整理
    手动创建单例
    Python 安装mysqldb
    Python UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-4: ordinal not in range(128)
  • 原文地址:https://www.cnblogs.com/vachester/p/6732054.html
Copyright © 2011-2022 走看看