题目描述
一些学校连入一个电脑网络。那些学校已订立了协议:每个学校都会给其它的一些学校分发软件(称作“接受学校”)。注意即使 B 在 A 学校的分发列表中, A 也不一定在 B 学校的列表中。
你要写一个程序计算,根据协议,为了让网络中所有的学校都用上新软件,必须接受新软件副本的最少学校数目(子任务 A)。更进一步,我们想要确定通过给任意一个学校发送新软件,这个软件就会分发到网络中的所有学校。为了完成这个任务,我们可能必须扩展接收学校列表,使其加入新成员。计算最少需要增加几个扩展,使得不论我们给哪个学校发送新软件,它都会到达其余所有的学校(子任务 B)。一个扩展就是在一个学校的接收学校列表中引入一个新成员。
输入输出格式
输入格式:
输入文件的第一行包括一个整数 N:网络中的学校数目(2 <= N <= 100)。学校用前 N 个正整数标识。
接下来 N 行中每行都表示一个接收学校列表(分发列表)。第 i+1 行包括学校 i 的接收学校的标识符。每个列表用 0 结束。空列表只用一个 0 表示。
输出格式:
你的程序应该在输出文件中输出两行。
第一行应该包括一个正整数:子任务 A 的解。
第二行应该包括子任务 B 的解。
输入输出样例
输入样例#1: 复制
5
2 4 3 0
4 5 0
0
0
1 0
输出样例#1: 复制
1
2
说明
题目翻译来自NOCOW。
USACO Training Section 5.3
思路:这个题目是要是用链式前向星+tarjan+缩点+强连通来解决的,咱们先来看看这道题的思路,简单来说这俩问第一问求的是缩点后入度为0的点的个数,第二问求得是缩点后Max(入度为0的数目,出度为0的数目),下面来解释下原因,对于第一问,要使图连通必须从入度为0的点开始,所求为其个数,第二问则是最少添加多少条边可以使图为为强连通图,可以知道只要把所有的出度为0的点和入度为0的点俩俩相连便可,即求俩者最大值,记住特判,因为若是开始给出的图为强连通图,那么缩点后max(1,1) = 1明显不符合题意,下面看代码
#include<iostream>
using namespace std;
const int maxn = 1100;
int cnt = 1, n, total, sum, index, ans1, ans2, ans, k;
int head[maxn], outdu[maxn], indu[maxn], dfn[maxn], low[maxn], Stack[maxn], vis[maxn], belong[maxn];
struct Node{
int to;
int next;
}node[2 * maxn];
void add(int u, int v) {
node[cnt].to = v;
node[cnt].next = head[u];
head[u] = cnt++;
}
void tarjan(int u) {
dfn[u] = low[u] = ++total;
Stack[++index] = u;
vis[u] = 1;
for (int i = head[u]; i != 0; i = node[i].next) {
int v = node[i].to;
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (vis[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if (low[u] == dfn[u]) {
++sum;
do {
int p = Stack[index--];
vis[p] = 0;
belong[p] = sum;
} while (Stack[index + 1] != u);
}
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
while (cin >> k && k) {
add(i, k);
}
}
for (int i = 1; i <= n; i++) {
if (!dfn[i]) {
tarjan(i);
}
}
for (int i = 1; i <= n; i++) {
for (int j = head[i]; j != 0; j = node[j].next) {
int v = node[j].to;
if (belong[v] != belong[i]) { //判断是不是同一个缩点
indu[belong[v]]++; //入度
outdu[belong[i]]++; //出度
}
}
}
for (int i = 1; i <= sum; i++) {
if (!indu[i]) ans1++;
if (!outdu[i]) ans2++;
}
ans = max(ans1, ans2);
if (sum == 1) {
cout << 1 << endl;
cout << 0;
} else {
cout << ans1 << endl;
cout << ans << endl;
}
return 0;
}