天天看點

NYOJ-120 校園網絡(強連通縮點targan算法)

校園網絡

時間限制: 3000 ms  |  記憶體限制: 65535 KB 難度: 5  

描述

南陽理工學院共有M個系,分别編号1~M,其中各個系之間達成有一定的協定,如果某系有新軟體可用時,該系将允許一些其它的系複制并使用該軟體。但該允許關系是單向的,即:A系允許B系使用A的軟體時,B未必一定允許A使用B的軟體。

現在,請你寫一個程式,根據各個系之間達成的協定情況,計算出最少需要添加多少個兩系之間的這種允許關系,才能使任何一個系有軟體使用的時候,其它所有系也都有軟體可用。

輸入

第一行輸入一個整數T,表示測試資料的組數(T<10)

每組測試資料的第一行是一個整數M,表示共有M個系(2<=M<=100)。

随後的M行,每行都有一些整數,其中的第i行表示系i允許這幾個系複制并使用系i的軟體。每行結尾都是一個0,表示本行輸入結束。如果某個系不允許其它任何系使用該系軟體,則本行隻有一個0.

輸出
對于每組測試資料,輸出最少需要添加的這種允許關系的個數。
樣例輸入
1
5
2 4 3 0
4 5 0
0
0
1 0      
樣例輸出
2      
來源
POJ改編
解析(轉):
Tarjan算法詳解

【功能】

      Tarjan算法的用途之一是,求一個有向圖G=(V,E)裡極大強連通分量。強連通分量是指有向圖G裡頂點間能互相到達的子圖。而如果一個強連通分量已經沒有被其它強通分量完全包含的話,那麼這個強連通分量就是極大強連通分量。

【算法思想】

      用dfs周遊G中的每個頂點,通dfn[i]表示dfs時達到頂點i的時間,low[i]表示i所能直接或間接達到時間最小的頂點。(實際操作中low[i]不一定最小,但不會影響程式的最終結果)

      程式開始時,order初始化為0,在dfs周遊到v時,low[v]=dfn[v]=order++,

v入棧(這裡的棧不是dfs的遞歸時系統弄出來的棧)掃描一遍v所能直接達到的頂點k,如果 k沒有被通路過那麼先dfs周遊k,low[v]=min(low[v],low[k]);如果k在棧裡,那麼low[v]=min(low[v],dfn[k])(就是這裡使得low[v]不一定最小,但不會影響到這裡的low[v]會小于dfn[v])。掃描完所有的k以後,如果low[v]=dfn[v]時,棧裡v以及v以上的頂點全部出棧,且剛剛出棧的就是一個極大強連通分量。

【大概的證明】

 1.  在棧裡,當dfs周遊到v,而且已經周遊完v所能直接到達的頂點時,low[v]=dfn[v]時,v一定能到達棧裡v上面的頂點:

       因為當dfs周遊到v,而且已經dfs遞歸調用完v所能直接到達的頂點時(假設上面沒有low=dfn),這時如果發現low[v]=dfn[v],棧上面的頂點一定是剛才從頂點v遞歸調用時進棧的,是以v一定能夠到達那些頂點。

2 .dfs周遊時,如果已經周遊完v所能直接到達的頂點而low[v]=dfn[v],我們知道v一定能到達棧裡v上面的頂點,這些頂點的low一定小于 自己的dfn,不然就會出棧了,也不會小于dfn[v],不然low [v]一定小于dfn[v],是以棧裡v以其v以上的頂點組成的子圖是一個強連通分量,如果它不是極大強連通分量的話low[v]也一定小于dfn[v](這裡不再詳細說),是以棧裡v以其v以上的頂點組成的子圖是一個極大強連通分量。

【時間複雜度】

     因為所有的點都剛好進過一次棧,所有的邊都通路的過一次,是以時間複雜度為O(n+m)

代碼如下:

1 // 強連通分量縮點
  2 #include <iostream>
  3 #include <cstring>
  4 #include <cstdio>
  5 #include <stack>
  6 
  7 using namespace std;
  8 
  9 const int MAX = 105;
 10 int map[MAX][MAX];
 11 int low[MAX], DFN[MAX], IN[MAX], OUT[MAX], instack[MAX], t[MAX];
 12 int n, order, res, ans;
 13 stack<int> S;
 14 
 15 void init()
 16 {
 17     memset(map, 0, sizeof(map));
 18     memset(low, 0, sizeof(low));
 19     memset(DFN, 0, sizeof(DFN));
 20     memset(IN, 0, sizeof(IN));
 21     memset(OUT, 0, sizeof(OUT));
 22     memset(instack, 0, sizeof(instack));
 23     memset(t, 0, sizeof(t));
 24     while(!S.empty())
 25        S.pop();
 26     res = 0;
 27     order = 0;
 28 }
 29 
 30 int min(int x, int y)
 31 {
 32     return x < y ? x : y;
 33 }
 34 
 35 void tr(int u)
 36 {
 37     int v;
 38     DFN[u] = low[u] = ++order;
 39     instack[u] = 1;
 40     S.push(u);
 41     for (int i = 1; i <= n; i++)
 42     {
 43         if(map[u][i])
 44         {
 45             if(!DFN[i])
 46             {
 47                 tr(i);
 48                 low[u] = min(low[u], low[i]);
 49             }
 50             else if(instack[i])
 51                 low[u] = min(low[u], DFN[i]);
 52         }
 53     }
 54     if(DFN[u] == low[u])
 55     {
 56         ++res;    // res 代表強連通分量的個數
 57         do
 58         {
 59             v=S.top();
 60             S.pop();
 61             instack[v] = 0;
 62             t[v] = res;
 63         }while(v != u);
 64     }
 65 }
 66 
 67 void tarjan()
 68 {
 69     for (int i = 1; i <= n; i++)
 70        if(!DFN[i])
 71            tr(i);
 72 }
 73 
 74 void solve()
 75 {
 76     for (int i = 1;i <= n; i++)
 77     {
 78         for (int j = 1;j <= n; j++)
 79             if(map[i][j])  // 統計每個強連通分量縮點的入度和出度
 80             {
 81                 ++IN[t[i]];
 82                 ++OUT[t[j]];
 83             }
 84     }
 85     int xx, yy;
 86     xx = yy = 0;
 87     for(int i = 1; i <= res; i++)
 88     {
 89         if(IN[i]==0)
 90            xx++;
 91         else if(OUT[i]==0)
 92            yy++;
 93     }
 94     ans = xx > yy ? xx : yy; // 結果為縮點後的有向圖中出度為0或者入度為0中的大者
 95 }
 96 
 97 int main()
 98 {
 99     int T, x;
100     scanf("%d", &T);
101     while (T--)
102     {
103         init();
104         scanf("%d",&n);
105         for (int i = 1; i <= n; i++)
106         {
107             while (scanf("%d", &x), x)
108                 map[i][x] = 1;
109         }
110         tarjan();
111         solve();
112         if(res == 1)
113            printf("0\n");
114         else
115            printf("%d\n", ans);
116     }
117     return 0;
118 }      

注:部分用到強連通分量的題目總結:

POJ 2186 Popular Cows (基礎)

POJ 2553 The Bottom of a Graph(alpc OJ 1274)(基礎)

POJ 1236 Network of Schools (基礎)

2010中南賽 light sources(alpc OJ) (基礎)

POJ 2762 Going from u to v or from v to u? (中等,弱連通分量 )

POJ 3160 Father Christmas flymouse(難,DP題)

POJ 1904 King‘s Quest(難,推薦,非縮點,比對思想與強連通分量的轉化)