最小樹形圖(模板)


本文鏈接:http://www.cnblogs.com/Ash-ly/p/5536796.html

定義:

  設G = (V, E)是一個有向圖,如果具有下述性質

  (1)G中不包含有向環;

  (2)存在一個頂點vi,它不是任何弧的終點,而V的其它頂點都恰好是唯一的一條弧的終點.則稱 G是以vi為根的樹形圖.

  最小樹形圖就是有向圖G = (V, E)中以vi為根的樹形圖中權值的和最小的那一個.

用朱劉算法求最小樹形圖:

  最小樹形圖基於貪心和縮點的思想,所謂縮點,就是將幾個點看成一個點,所有連到這幾個點的邊都視為連到收縮點,所有從這幾個點連出的邊都視為從收縮點連出.這里假設根節點為V0.

  (1)求最短弧集合E0.

  從所有以Vi(i ≠ 0)為終點的弧中取一條最短的,若對於點i,沒有入邊,則不存在最小樹形圖,算法結束;如果能取,則得到由n個點和n-1條邊組成的圖G的一個子圖G',這個子圖的權值一定是最小的,但是不一定是一棵樹.

  (2)檢查E0.

  若E0沒有有向環且不包含收縮點,則計算結束,E0就是G以V0為根的最小樹形圖,若E0沒有有向環,但是存在收縮點,轉到步驟(4),若E0含有有向環,則轉入步驟(3).

  (3)收縮G中的有向環.

  把G中的環C收縮成點u,對於圖G中兩端都屬於C的邊就會被收縮掉,其他弧仍然保留,得到一個新的圖G1,G1中以收縮點為終點的弧的長度要變化,變化的規則是:設點v在環C中,且環中指向v的邊的權值為w,點v'不在環C中,則對於G中的每一條邊<v', v>,在G1中有邊<v', u>和其對應,且權值WG1(<v', u>) = WG(<v', v>) - w;對於圖G中以環C中的點為起點的邊<v', v>,在圖G1中有邊<u, v'>,則WG1(<u, v'>) = WG(<v', v>).有一點需要注意,在這里生成的圖

G1可能存在重邊.

  對於圖G和G1:

  <1>:如果圖G1中沒有以v0為根的最小樹形圖,則圖G也沒有.

  <2>:如果G1中有一v0為根的最小樹形圖,則可按照步驟(4)的展開方法得到圖G的最小樹形圖.

  所以,應該對於圖G1代到(1)中反復求其最小樹形圖,直到G1的最小樹形圖u求出.

  (4)展開收縮點.

  假設圖G1的最小樹形圖為T1,那么所有T1中的弧都屬於圖G的最小樹形圖T.將G1的一個收縮點u展開成環C,從C中去掉與T1具有相同終點的弧,其他弧都屬於T.

  總結一下,為了求一個圖的最小樹形圖,先求出最短弧集合E0.如果E0不存在,則圖的最小樹形圖也不存在.如果E0存在且不具有環,則E0就是最小樹形圖.如果E0存在但是存在有向環,則把這個環收縮成一個點u,形成新的圖G1,然后對於G1繼續求其的最小樹形圖,直到求到圖Gi.如果Gi不具有最小樹形圖,那么此圖不存在最小樹形圖,如果Gi存在最小樹形圖,那么逐層展開,就得到了原圖的最小樹形圖.

下面有張圖:

  第一幅圖為原始圖G,首先對於圖G求其最短弧集合E0,即第二幅圖G1.然后檢查E0是滿足條件,在這里,可以看到G1具有兩個環,那么把這兩個環收縮,如第三幅圖所示,U1,U2分別為收縮后的點,然后對應的權值進行更新,起點是環中的點,終點是環外的點,則權值不變,反之,起點是環外的點,終點是環內的點,則權值應該減去E0中指向環內點的權值,形成新的圖,如第三幅圖.對於其反復求最小樹形圖,直到不存在最小樹形圖,或者求得縮點后的圖的最小樹形圖,然后展開就好了,如第六幅圖.

  如果只要求計算權值的話,則不需要展開,所有環中權值的和加上其他各個點與點之間,或者收縮點和點之間的權值就是總的權值.

代碼:

 1 #include <iostream>
 2 #include <cmath>
 3 #include <cstdio>
 4 #include <cstring>
 5 #include <cstdlib>
 6 #include <algorithm>
 7 #include <string>
 8 typedef long long LL;
 9 using namespace std;
10 const int MAXV = 100;
11 const int MAXE = 10000;
12 const int INF = 0x3f3f3f3f;
13 
14 //求具有V個點,以root為根節點的圖map的最小樹形圖
15 int zhuliu(int root, int V, int map[MAXV + 7][MAXV + 7]){
16     bool visited[MAXV + 7];
17     bool flag[MAXV + 7];//縮點標記為true,否則仍然存在
18     int pre[MAXV + 7];//點i的父節點為pre[i]
19     int sum = 0;//最小樹形圖的權值
20     int i, j, k;
21     for(i = 0; i <= V; i++) flag[i] = false, map[i][i] = INF;
22     pre[root] = root;
23     while(true){
24         for(i = 1; i <= V; i++){//求最短弧集合E0
25             if(flag[i] || i == root) continue;
26             pre[i] = i;
27             for(j = 1; j <= V; j++)
28                 if(!flag[j] && map[j][i] < map[pre[i]][i])
29                     pre[i] = j;
30             if(pre[i] == i) return -1;
31         }
32         for(i = 1; i <= V; i++){//檢查E0
33             if(flag[i] || i == root) continue;
34             for(j = 1; j <= V; j++) visited[j] = false;
35             visited[root] = true;
36             j = i;//從當前點開始找環
37             do{
38                 visited[j] = true;
39                 j = pre[j];
40             }while(!visited[j]);
41             if(j == root)continue;//沒找到環
42             i = j;//收縮G中的有向環
43             do{//將整個環的取值保存,累計計入原圖的最小樹形圖
44                 sum += map[pre[j]][j];
45                 j = pre[j];
46             }while(j != i);
47             j = i;
48             do{//對於環上的點有關的邊,修改其權值
49                 for(k = 1; k <= V; k++)
50                     if(!flag[k] && map[k][j] < INF && k != pre[j])
51                         map[k][j] -= map[pre[j]][j];
52                 j = pre[j];
53             }while(j != i);
54             for(j = 1; j <= V; j++){//縮點,將整個環縮成i號點,所有與環上的點有關的邊轉移到點i
55                 if(j == i) continue;
56                 for(k = pre[i]; k != i; k = pre[k]){
57                     if(map[k][j] < map[i][j]) map[i][j] = map[k][j];
58                     if(map[j][k] < map[j][i]) map[j][i] = map[j][k];
59                 }
60             }
61             for(j = pre[i]; j != i; j = pre[j]) flag[j] = true;//標記環上其他點為被縮掉
62             break;//當前環縮點結束,形成新的圖G',跳出繼續求G'的最小樹形圖
63         }
64         if(i > V){//如果所有的點都被檢查且沒有環存在,現在的最短弧集合E0就是最小樹形圖.累計計入sum,算法結束
65             for(i = 1; i <= V; i++)
66                 if(!flag[i] && i != root) sum += map[pre[i]][i];
67             break;
68         }
69     }
70     return sum;
71 }

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM