1. 程式人生 > >次小生成樹的學習

次小生成樹的學習

轉載:http://www.cnblogs.com/hxsyl/p/3290832.html 
 為什麼寫這個呢?因為那天聽到了這個詞,屬於MST的擴充套件……最小K度樹有空研究。

一.理論準備

        需要讀者事先懂得prime演算法,不太瞭解的請看博主這一篇http://www.cnblogs.com/hxsyl/p/3286956.html,也需要讀者對DP瞭解一些。

        先看一個結論:次小生成樹可由最小生成樹換一條邊得到,筆者認為很有必要搞清楚這一點,,否則對演算法理解不夠深入。

         證明:咱換種方式去看待這個結論(一個生成樹可以通過換邊得到另一個生成樹),T是某一棵最小生成樹,T0是任一棵異於T的生成樹,通過變換T0 --> T1 --> T2 --> ... --> Tn (T)  變成最小生成樹。所謂的變換是,每次把Ti中的某條邊換成T中的一條邊, 而且樹T(i+1)的權小於等於Ti的權。

         看下面的具體步驟(一定要理解透徹)。 
         step 1. 在Ti中任取一條不在T中的邊uv. 
         step 2. 把邊uv去掉,就剩下兩個連通分量A和B,在T中,必有唯一的邊u'v' 連結A和B。這是為什麼呢?因為生成樹中任意兩點間只有一條路徑(下面也要用這個),且必有一條。 
         step 3. 顯然u'v'的權比uv小 (prime演算法貪心的,否則,uv就應該在T中),把u'v'替換uv即得樹T(i+1)。 
         特別地:取T0為任一棵次小生成樹,T(n-1) 也就是次小生成樹且跟T差一條邊, 結論得證。

         下面看具體演算法。

         step 1.  先用prim求出最小生成樹T,在prim的同時,用一個矩陣maxd[u][v] 記錄 在T中連結任意兩點u,v的唯一的路中權值最大的那條邊的權值.(有些拗口),這是很容易做到的,因為prim是每次增加一個結點s, 在此需要儲存節點和其父節點,採用DP,則最大權值要麼是新加入的邊,要麼是父節點到起始點的採用DP算出來的距離,如下:
//u是剛加入的點,不過還沒進入節點陣列,v是已經存在的點
//min是按prime新加入那條邊
maxd[v][u] = maxd[u][v] = max{min,maxd[father[u]][v]}
//u是剛加入的點,不過還沒進入節點陣列,v是已經存在的點//min是按prime新加入那條邊maxd[v][u] = maxd[u][v] = max{min,maxd[father[u]][v]} 該步驟用時 O(V^2),就是prime演算法的耗時。 step 2. 列舉所有不在T中的邊uv, 加入邊uv則必然替換權為maxd[u][v]的邊,這樣才能保證次小。二.演算法實現 以POJ1679為例,判斷最小生成樹是否唯一(不唯一可能是重邊,不過一般在做題裡不可能,否則沒法建圖,另外就是一般情況了,看下圖)。 下面這三個圖都是MST,權值161。234
2
34 只要最小生成樹和次小生成樹權值和一樣就唯一。因此得出如下演算法,首先計算出最小生成樹T,然後對最小生成樹上任意不相鄰的兩個點 uv新增最小生成樹以外的存在的邊形成環,然後尋找u與v之間最小生成樹上最長的邊刪去,計算map[i][j]與 maxd[i][j差值,求出最小的來,如果是0,就說明MST和次小生成樹一樣。
//頂點數100,看成了1000,一個MLE,改了立馬AC,嘿嘿
//這道題目,AC率很低
import java.util.Scanner;
public class POJ1679 {
  static int maxn = 105;
  static int[][] map = new int[maxn][maxn];
  static int[][] maxd = new int[maxn][maxn];
  static int[] father = new int[maxn];
  static int[] dist = new int[maxn];
  static boolean[] vis = new boolean[maxn];
  static int n,m;
  public static void main(String[] args) {
    Scanner sc= new Scanner(System.in);
    int num = sc.nextInt();
    int u,v,w;
    while(num-->0) {
      n = sc.nextInt();
      m = sc.nextInt();
      for(int i=1; i<=n; i++) {
        for(int j=1; j<=n; j++) {
          if(i==j) {
            map[i][j] = 0;
          }else {
            map[i][j] = 0x3f3f3f3f;
          }
          maxd[i][j] = -1;
        }
      }
      for(int i=0; i<m; i++) {
        u = sc.nextInt();
        v = sc.nextInt();
        w = sc.nextInt();
        map[u][v] = w;
        map[v][u] = w;
      }
      int ans = prime();
      int min = 0x3f3f3f3f;
      for(int i=1; i<=n; i++) {
        for(int j=1; j<=n; j++) {
          boolean tag = i!=j&&map[i][j]!=0x3f3f3f3f
              &&father[i]!=j&&father[j]!=i;
          if(tag) {
            if(min>map[i][j]-maxd[i][j]) {
              min = map[i][j]-maxd[i][j];
            }
          }
        }
      }
      if(0==min) {
        System.out.println("Not Unique!");
      }else {
        System.out.println(ans);
      }
    }
  }
  private static int prime() {
    int ans = 0;
    for(int i=1; i<=n; i++) {
      dist[i] = map[1][i];
      father[i] = 1;
      vis[i] = false;
    }
    vis[1] = true;
    //存放MST節點
    int stack[] = new int[n+1];
    int top = 0;
    stack[top++] = 1;
    for(int i=1; i<n; i++) {
      int next = 1;
      int min = 0x3f3f3f3f;
      for(int j=1; j<=n; j++) {
        if(!vis[j]&&min>dist[j]) {
          next = j;
          min = dist[j];
        }
      }
      vis[next] = true;
      ans += min;
      //dp
      for(int k=0; k<top; k++) {
        maxd[next][stack[k]] = maxd[stack[k]][next]
            = Math.max(min,maxd[father[next]][stack[k]]);
      }
      stack[top++] = next;
      for(int t=1; t<=n; t++) {
        if(!vis[t]&&dist[t]>map[next][t]) {
          dist[t] = map[next][t];
          father[t] = next;
        }
      }
    }
    return ans;
  }
}
作者:張朋飛
出處:http://www.cnblogs.com
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利.