1. 程式人生 > >POJ1236_A - Network of Schools _強連通分量::Tarjan算法

POJ1236_A - Network of Schools _強連通分量::Tarjan算法

The clu i+1 while distrib 初始 -s contains stream

Time Limit: 1000MS Memory Limit: 10000K

Description

A number of schools are connected to a computer network. Agreements have been developed among those schools: each school maintains a list of schools to which it distributes software (the “receiving schools”). Note that if B is in the distribution list of school A, then A does not necessarily appear in the list of school B
You are to write a program that computes the minimal number of schools that must receive a copy of the new software in order for the software to reach all schools in the network according to the agreement (Subtask A). As a further task, we want to ensure that by sending the copy of new software to an arbitrary school, this software will reach all schools in the network. To achieve this goal we may have to extend the lists of receivers by new members. Compute the minimal number of extensions that have to be made so that whatever school we send the new software to, it will reach all other schools (Subtask B). One extension means introducing one new member into the list of receivers of one school.

Input

The first line contains an integer N: the number of schools in the network (2 <= N <= 100). The schools are identified by the first N positive integers. Each of the next N lines describes a list of receivers. The line i+1 contains the identifiers of the receivers of school i. Each list ends with a 0. An empty list contains a 0 alone in the line.

Output

Your program should write two lines to the standard output. The first line should contain one positive integer: the solution of subtask A. The second line should contain the solution of subtask B.

Sample Input

5
2 4 3 0
4 5 0
0
0
1 0

Sample Output

1
2

題意

有向圖上有 N 個點,若幹有向邊。
第一問:至少給幾個點傳遞信息,才能保證信息傳遍整個圖。


第二問:至少添加幾條邊,才能使任意選擇點,都能傳遍整個圖。

思路

強連通分量的裸題。
強連通分量內的任意一點收到消息,內部其他各點必定都能收到消息。因此,可以把每個強連通分量縮成一個點。只需要考察入度為 0 的強連通分量的個數,就是第一問的答案。
對於第二問,是把圖連接成一個強連通分量,同樣可以在縮點後的圖中操作。這裏的做法是統計圖中入度為0、出度為0的強連通分量的個數,取較大值即為第二問的答案。 本題中原圖只有一個強連通分量的情況需要特判。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<vector>
  5 
  6 using namespace std;
  7 
  8 const int maxn = 100 + 10;
  9 
 10 int N;
 11 int In[maxn], Out[maxn];
 12 
 13 /***************************Tarjan算法模板***************************/
 14 vector<int> G[maxn];
 15 int Mark[maxn], Root[maxn], Stack[maxn];//時間戳,根(當前分量中時間戳最小的節點),棧
 16 bool Instack[maxn]; //是否在棧中標記
 17 int Ssc[maxn];            //每個節點所在的強連通分量的編號
 18 int Index, Ssc_n, Top;    //搜索時用的時間戳,強連通分量總數,棧頂指針
 19 
 20 void Tarjan(int u)        //u 當前搜索到的點
 21 {
 22     Mark[u] = Root[u] = ++ Index;  //每找到一個點,對時間戳和根初始化
 23     Stack[Top ++] = u;        //壓棧
 24     Instack[u] = true;        //在棧中標記
 25 
 26     int v;
 27 
 28     for(int i= 0; i< G[u].size(); i++)   //向下搜索
 29     {
 30         v = G[u][i];
 31         if(Mark[v] == 0)                 //沒到過的點
 32         {
 33             Tarjan(v);  //先向下搜索
 34             if(Root[u] > Root[v]) Root[u] = Root[v];//更新根
 35         }
 36         else if(Instack[v] && Root[u] > Mark[v]) Root[u] = Mark[v];  //到過的點且點仍在棧中,試著看這個點能不能成為根
 37     }
 38 /*對當前點的搜索結束*/
 39     if(Mark[u] == Root[u])  //當前點本身時根
 40     {
 41         Ssc_n ++;   //更新強連通分量數
 42 
 43         do{    //棧中比它後入棧的元素在以它為根的強連通分量中
 44             v = Stack[-- Top];
 45             Instack[v] = false;
 46             Ssc[v] = Ssc_n;//把同一個強連通分支的點做上相同標記 
 47         }while(v != u);    //直到它自己
 48     }
 49 }
 50 
 51 void SSC()
 52 {
 53     memset(Mark, 0, sizeof Mark);   //初始化時間戳和棧內標記
 54     memset(Instack, false, sizeof Instack);
 55     Index = Ssc_n = Top = 0;   //初始化時間戳,強連通分量數,棧頂指針
 56 
 57     for(int i= 1; i<= N; i++)   //保證圖上所有點都訪問到
 58         if(Mark[i] == 0) Tarjan(i);
 59 }
 60 /***************************Tarjan算法模板***************************/
 61 
 62 int main()
 63 {
 64     //freopen("in.txt", "r", stdin);
 65 
 66     scanf("%d", &N);
 67     for(int i= 1; i<= N; i++)
 68     {
 69         int x;
 70         while(scanf("%d", &x), x)
 71             G[i].push_back(x);
 72     }
 73 
 74     SSC();
 75 
 76     if(Ssc_n == 1)  //只有一個強連通分量的情況
 77     {
 78         cout << "1\n0\n";
 79         return 0;
 80     }
 81 
 82     memset(In, 0, sizeof In); //求每個強連通分量的入度和出度
 83     memset(Out, 0, sizeof Out);
 84     for(int u= 1; u<= N; u++)
 85     {
 86         for(int i= 0; i< G[u].size(); i++)
 87         {
 88             int v = G[u][i];
 89             if(Ssc[u] != Ssc[v])//u,v兩點不在同一個強連通分支 
 90                 Out[Ssc[u]] ++, In[Ssc[v]] ++;
 91         }
 92     }
 93 
 94     int S1 = 0, S2 = 0;//找入度為0、出度為0的點的數目
 95     for(int i= 1; i<= Ssc_n; i++)
 96     {
 97         if(In[i] == 0) S1 ++;
 98         if(Out[i] == 0) S2 ++;
 99     }
100 
101     cout << S1 << endl << max(S1, S2) << endl;
102 
103     return 0;
104 }

POJ1236_A - Network of Schools _強連通分量::Tarjan算法