模型概述 有一DAG,問最少加多少條邊能夠使圖強連通。 題目描述 一些學校連入一個電腦網路。那些學校已訂立了協議:每個學校都會給其它的一些學校分發軟體(稱作“接受學校”)。註意即使 B 在 A 學校的分發列表中, A 也不一定在 B 學校的列表中。 你要寫一個程式計算,根據協議,為了讓網路中所有的學 ...
模型概述
有一DAG,問最少加多少條邊能夠使圖強連通。
題目描述
一些學校連入一個電腦網路。那些學校已訂立了協議:每個學校都會給其它的一些學校分發軟體(稱作“接受學校”)。註意即使 B 在 A 學校的分發列表中, A 也不一定在 B 學校的列表中。
你要寫一個程式計算,根據協議,為了讓網路中所有的學校都用上新軟體,必須接受新軟體副本的最少學校數目(子任務 A)。更進一步,我們想要確定通過給任意一個學校發送新軟體,這個軟體就會分發到網路中的所有學校。為了完成這個任務,我們可能必須擴展接收學校列表,使其加入新成員。計算最少需要增加幾個擴展,使得不論我們給哪個學校發送新軟體,它都會到達其餘所有的學校(子任務 B)。一個擴展就是在一個學校的接收學校列表中引入一個新成員。
輸入輸出格式
輸入格式:
輸入文件的第一行包括一個整數 N:網路中的學校數目(2 <= N <= 100)。學校用前 N 個正整數標識。
接下來 N 行中每行都表示一個接收學校列表(分發列表)。第 i+1 行包括學校 i 的接收學校的標識符。每個列表用 0 結束。空列表只用一個 0 表示。
輸出格式:
你的程式應該在輸出文件中輸出兩行。
第一行應該包括一個正整數:子任務 A 的解。
第二行應該包括子任務 B 的解。
題目分析
第一問非常簡單,就是縮點之後求入度為零的點個數。
至於第二問……一開始我想了很久,後來發現好像想複雜去了。
先加邊再刪邊
我一開始想到的是類似於floyd的想法,來試圖刪去“多餘的邊”。
這裡對於多餘的邊的定義是:刪去這些邊後不會改變圖中兩兩點對的連通性。
那麼對於點$x$,將它與所有不能到達的點$y$連邊,最後再考慮哪些邊是可刪去的多餘邊。
但是這樣很冗餘,同時計算出的答案是會偏大的……而且我後來發現好像floyd做不到這個操作?
從圖的性質考慮
先不來考慮森林(其實森林的情況也一樣)比如說這樣一張圖:
標紅的是入度為零的點;標藍的是出度為零的點。
按照之前的想法,那就是把點1,2,3,4與其他所有它們不能到達的點相連。
但是可以發現對於點5,6來說,是能夠到達所有點的;對於點2來說,它只能夠到達自身;並且,所有點都能夠到達點2。
那麼只需要給點2向點5,6連邊,就能夠使圖成為強連通。
更為普遍的情況則是,紅點有$x$個;藍點有$y$個,則最少加邊數為$max\{x,y\}$。
1 #include<bits/stdc++.h> 2 const int maxn = 103; 3 const int maxm = 20003; 4 5 int tim,dfn[maxn],low[maxn],degOut[maxn],degInn[maxn]; 6 int stk[maxn],cnt; 7 int col[maxn],cols; 8 int edgeTot,edges[maxm],nxt[maxm],head[maxn]; 9 int n,ans; 10 11 int read() 12 { 13 char ch = getchar(); 14 int num = 0; 15 bool fl = 0; 16 for (; !isdigit(ch); ch = getchar()) 17 if (ch=='-') fl = 1; 18 for (; isdigit(ch); ch = getchar()) 19 num = (num<<1)+(num<<3)+ch-48; 20 if (fl) num = -num; 21 return num; 22 } 23 void addedge(int u, int v) 24 { 25 edges[++edgeTot] = v, nxt[edgeTot] = head[u], head[u] = edgeTot; 26 } 27 void tarjan(int x) 28 { 29 dfn[x] = low[x] = ++tim; 30 stk[++cnt] = x; 31 for (int i=head[x]; i!=-1; i=nxt[i]) 32 { 33 int v = edges[i]; 34 if (!dfn[v]) 35 tarjan(v), 36 low[x] = std::min(low[x], low[v]); 37 else if (!col[v]) 38 low[x] = std::min(low[x], dfn[v]); 39 } 40 if (low[x]==dfn[x]){ 41 col[x] = ++cols; 42 for (; stk[cnt]!=x; cnt--) 43 col[stk[cnt]] = cols; 44 cnt--; 45 } 46 } 47 int main() 48 { 49 memset(head, -1, sizeof head); 50 n = read(); 51 for (int i=1; i<=n; i++) 52 { 53 int x; 54 while (x = read()) 55 addedge(i, x); 56 } 57 for (int i=1; i<=n; i++) 58 if (!dfn[i]) tarjan(i); 59 for (int i=1; i<=n; i++) 60 for (int j=head[i]; j!=-1; j=nxt[j]) 61 if (col[i]!=col[edges[j]]) 62 degInn[col[i]]++, degOut[col[edges[j]]]++; 63 for (int i=1; i<=cols; i++) 64 if (!degOut[i]) ans++; 65 printf("%d\n",ans); 66 if (cols==1) printf("0\n"); 67 else{ 68 int cnta = 0, cntb = 0; 69 for (int i=1; i<=cols; i++) 70 { 71 if (degInn[i]==0) cnta++; 72 if (degOut[i]==0) cntb++; 73 } 74 printf("%d\n",std::max(cnta, cntb)); 75 } 76 return 0; 77 }
END