题意:给出一个含有n(2 <= n <= 100)个点的有向图,求从至少几个点出发可以遍历整个图,和加几条边可以使得从任意一个点出发都能遍历整个图。
分析:
我AC的第一个强连通分量的题目。
关于tarjan强连通分量算法网上很多,不多介绍。
这里说说我代码的习惯,用v数组来记录是否在栈里,v(i) == 2说明已经出栈,v(i) == 1说明在栈里,v(i) == 0说明未被加进过栈中,这题的v数组用的是inst.
bl数组是记录结点所属的强连通分量的编号。
有一个不懂的地方是后向边的处理,zrt说dfn换成low可能会被特殊数据卡掉,然而我想了半天也想不到什么特殊数据,换成low也能AC,总之记住,背过就好了。
那回到这道题。
原图缩点之后变成了若干条DAG,那么从每个入度为0的点出发一定能够遍历整张图。
第二个问题是要考虑从出度为0的点向入度为0的点连一条边,这样答案就是max(入度点数量, 出度点数量)
#include <cstdio>
#include <algorithm>
using namespace std;
const int mn = 105, mm = 10005;
int n, e, x, num, cur, tmp, cnt, ans1, ans2, hd[mn], to[mm], nxt[mm], dfn[mn], low[mn], st[mn], inst[mn], bl[mn], ru[mn], chu[mn];
void add(int x, int y) {
to[++e] = y;
nxt[e] = hd[x];
hd[x] = e;
}
void tarjan(int x) {
dfn[x] = low[x] = ++num, st[++cur] = x, inst[x] = 1;
for(int i = hd[x]; i; i = nxt[i])
if(!dfn[to[i]]) tarjan(to[i]), low[x] = min(low[x], low[to[i]]);
else if(inst[to[i]] == 1) low[x] = min(low[x], dfn[to[i]]);
if(low[x] == dfn[x]) {
cnt++;
do inst[tmp=st[cur--]] = 2, bl[tmp] = cnt; while(tmp != x);
}
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) while(scanf("%d", &x) && x) add(i, x);
for(int i = 1; i <= n; i++) if(!inst[i]) tarjan(i);
if(cnt == 1) {
printf("1\n0");
return 0;
}
for(int i = 1; i <= n; i++)
for(int j = hd[i]; j; j = nxt[j])
if(bl[i] != bl[to[j]]) ru[bl[to[j]]]++, chu[bl[i]]++;
for(int i = 1; i <= cnt; i++) {
if(!ru[i]) ans1++;
if(!chu[i]) ans2++;
}
printf("%d\n%d", ans1, max(ans1, ans2));
return 0;
}