題目鏈接:http://poj.org/problem?id=1236
題目大意:
有一個有向圖,圖中兩點之間若有一條有向邊代表一份軟件可以從弧尾發送到弧頭。
題目分兩個小問,第一個問題事發送軟件到圖中的某些點,問最少發送多少個點可以使這份軟件可以到達圖中的任何一個點
第二小問問,最少向圖中添加幾條邊可以使發送軟件到圖中任意一個點就可以使該軟件到達圖中的所有點。
思路:
求強聯通分量,一個強聯通分量可以當做一個點來看。第一問是求入度爲0的點的數量,第二問求入度爲0點的數量和出度爲0的點的數量中的最大值。
#include<iostream>
#include<cstring>
#include<stack>
using namespace std;
const int N = 105;
struct Edge{
int to, nx;
}e[N*N];
stack<int> stk;
int dfn[N], low[N], sccn[N], sccCnt, step;
int tot, head[N];
bool instack[N];
int in[N], out[N];
void initMap(){
memset(head, -1, sizeof(head));
memset(in, 0, sizeof(in));
memset(out, 0, sizeof(out));
tot = 0;
}
void initScc(){
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
memset(sccn, 0, sizeof(sccn));
memset(instack, false, sizeof(instack));
step = 0;
}
void addEdge(int from, int to){
e[++tot] = Edge{to, -1};
e[tot].nx = head[from];
head[from] = tot;
}
void tarjan(int u){
stk.push(u);
dfn[u] = low[u] = ++step;
instack[u] = true;
for (int k = head[u]; k != -1; k = e[k].nx){
int to = e[k].to;
if (!dfn[to]){
tarjan(to);
low[u] = min(low[u], low[to]);
}
else if (instack[to]){
low[u] = min(low[u], dfn[to]);
}
}
if (dfn[u] == low[u]){
int v;
sccCnt++;
while(true){
v = stk.top();
stk.pop();
instack[v] = false;
sccn[v] = sccCnt;
if (v == u) break;
}
}
}
void getScc(int n){
initScc();
for (int i = 1; i <= n; i++) if (!dfn[i]){
tarjan(i);
}
}
int main(){
std::ios::sync_with_stdio(false);
int n;
while(cin >> n){
initMap();
for (int i = 1; i <= n; i++){
int from, to;
from = i;
while(cin >> to && to){
addEdge(from, to);
}
}
initScc();
getScc(n);
for (int i = 1; i <= n; i++){
for (int k = head[i]; k != -1; k = e[k].nx){
int to = e[k].to;
if (sccn[to] != sccn[i]){
in[sccn[to]]++;
out[sccn[i]]++;
}
}
}
if (sccCnt == 1){
cout << "1" << endl;
cout << "0" << endl;
continue;
}
int ans1 = 0, ans2 = 0;
for (int i = 1; i <= sccCnt; i++){
if (!in[i]) ans1++;
if (!out[i]) ans2++;
}
cout << ans1 << endl;
cout << (ans1>ans2 ? ans1 : ans2) << endl;
}
return 0;
}