题目链接 http://poj.org/problem?id=1236
翻译一下题目吧,,,,
大致含义就是,有n个学校,现在要向n个学校传递一个软件,如果A学校愿意支援B学校,那么给了A,A就会给B,但是A支援B但是B不一定支援A (有向图警告) ,要求什么呢,最少给多少个学校就可以给到全部的学校,最少加几个支援关系,可以使得给任意一个学校就可以传递到全部学校去。
思路:
第一个问题吧,首先同一个强连通分量内的随意给一个学校就可以,因此可以将一个强连通分量看作是一个点处理 (缩点) ,缩点后,重新建图,一个点代表一个强连通分量,不在一个强连通分量内的,如果A中有一个点可以到达B内的一个点,那么相当于两个强连通分量有边连接A --> B,对于这样的关系,我们发现,只要给了A,A就会给B,也就是说,对于这样的一个链,只要给了第一个后面的就都有了,第一个怎么表示呢???对于A --> B -->C,发现,B和C都有入度,也就是说B和C都会有学校传递给他们,就不用管了,对于A而言,它没有入度,也就是说,如果你不给他,他就不可能会有的。所以只要统计入度为0的点的个数即可。
对于第二个问题,ahhhh,我还想了一小会儿呢,相当于将整个图的点,添加几条边后变成一个强连通分量,强连通分量内部的点一定满足每个点至少一个入度和一个出度,那么对于图中没有入度的点补上入度,没有出度的点,补上出度就可以构成一个强连通分量。要想最少话,肯定是自给自足了,就是把入度为0的点和出度为0的点连边,比如现在有n个点出入为0,有n个点入度为0,将这n个出度为0的点分别向n个出度为0的点连边,这样就补全了出度和入度为0的点,如果出度和入度为0的点数量不同,假设入度为0有m个,出度为0有n个,n > m,肯定还要补的首先从n个点中选m个与出度为0的点连边,还剩下n-m个点出度为0,只需要将这n-m个点向前面的n个补了入度的点连边就行了,如果入度大于出度,将原本出度为0的点多向入度为0的点连边就行了,所以答案应该是max(n, m)
特别地,如果只有一个强连通分量,第二个就不用统计了,直接是0就可以了。
/**
* Author : correct
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#define mem(a, b) memset(a, b, sizeof a)
using namespace std;
const int N = 10100;
int head[N], nex[N], to[N], cnt;
void add(int a, int b){
++cnt;
to[cnt] = b;
nex[cnt] = head[a];
head[a] = cnt;
}
int n;
int dfn[110], low[110];
bool vis[110];
int block[110];// 记录属于哪一个强连通分量
int st[110], top;
int num, q;
void pre_work(){
mem(head, -1);
mem(nex, - 1);
cnt = 0;
num = 0;
mem(dfn, 0);
mem(low, 0);
mem(vis, 0);
top = 0;
q = 0;
mem(block, 0);
}
void tarjan(int x){
dfn[x] = low[x] = ++num;
st[++top] = x;
vis[x] = 1;
for (int i = head[x]; ~i; i = nex[i]){
int y = to[i];
if (dfn[y] == 0){
tarjan(y);
low[x] = min(low[x], low[y]);
}
else if (vis[y])low[x] = min(low[x], dfn[y]);
}
if (low[x] == dfn[x]){
++q;
while (1){
int t = st[top--];
vis[t] = 0;
block[t] = q;
if (t == x)break;
}
}
}
bool c[110];
bool out[110];
int main(){
// 1.统计入读为0的点的个数
// 2.出度为0和入度为0点数的最大值,特别地,只有一个强连通分量的时候是0
pre_work();
ios::sync_with_stdio(false);
cin >> n;
for (int i = 1; i <= n; i++){
int x;
while (cin >> x && x){
add(i, x);
}
}
for (int i = 1; i <= n; i++){
if (dfn[i] == 0){
tarjan(i);
}
}
mem(c, 0);
mem(out, 0);
for (int i = 1; i <= n; i++){
for (int j = head[i]; ~j; j = nex[j]){
int y = to[j];
if (block[i] != block[y]){
c[block[y]] = 1;// 入度
out[block[i]] = 1;// 出度
}
}
}
int ans = 0;
int chu, ru;
chu = 0;
ru = 0;
for (int i = 1; i <= q; i++){
if (!c[i])++ans;
if (!c[i])ru++;
if (!out[i])chu++;
}
cout << ans << "\n";
cout << (q == 1 ? 0 : max(chu, ru)) << "\n";
return 0;
}