POJ1236 Network of Schools

题意:给出一个含有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;
}


你可能感兴趣的:(强连通分量tarjan)