POJ 2186 Popular Cows (强连通分量)

题目类型  强连通分量

题目意思
给出 n (n<=10000) 个点和 m(<=50000) 条有向边 问有多少个点满足 其他点到这个点都可达

解题方法
求出强连通分量后 把在同一个强连通分量中的点看成一个点(即缩成一个点)  这时点与点之间还可能存在有向边(其实就是桥) 
那么要达成题目的要求 则出度为 0 的点只能有一个 而这个点对应的强连通分量包含的点数即题目所求

参考代码 - 有疑问的地方在下方留言 看到会尽快回复的
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <vector>

using namespace std;

const int maxn = 1e4 + 10;

vector<int>G[maxn];
int pre[maxn], lowlink[maxn], sccno[maxn], dfs_clock, scc_cnt;
int d[maxn];
stack<int>S;

void dfs(int u) {
	pre[u] = lowlink[u] = ++dfs_clock;
	S.push(u);
	for( int i=0; i<G[u].size(); i++ ) {
		int v = G[u][i];
		if(!pre[v]) {
			dfs(v);
			lowlink[u] = min(lowlink[u], lowlink[v]);
		}
		else if(!sccno[v]) {
			lowlink[u] = min(lowlink[u], pre[v]);
		}
	}
	if(lowlink[u] == pre[u]) {
		scc_cnt++;
		for(;;) {
			int x = S.top(); S.pop();
			sccno[x] = scc_cnt;
			if(x == u) break;
		}
	}
}

void find_scc(int n) {
	dfs_clock = scc_cnt = 0;
	memset(sccno, 0, sizeof(sccno));
	memset(pre, 0, sizeof(pre));
	for( int i=1; i<=n; i++ ) {
		if(!pre[i]) dfs(i);
	}
}

int main() {
	freopen("in", "r", stdin);
	int n, m;
	while(scanf("%d%d", &n, &m) !=  EOF) {
		for( int i=1; i<=n; i++ ) G[i].clear();
		for( int i=0; i<m; i++ ) {
			int u, v;
			scanf("%d%d", &u, &v);
			G[u].push_back(v);
		}
		find_scc(n);
		memset(d, 0, sizeof(d));
		for( int i=1; i<=n; i++ ) {
			for( int j=0; j<G[i].size(); j++ ) {
				int v = G[i][j];
				if(sccno[i] != sccno[v]) {
					d[sccno[i]]++;
				}
			}
		}
		int ans = 0, nans = 1;
		for( int i=1; i<=scc_cnt; i++ ) {
			if(d[i] == 0) {
				ans++;
				nans = i;
			}
		}
		if(ans > 1) printf("0\n");
		else {
			ans = 0;
			for( int i=1; i<=n; i++ ) if(sccno[i] == nans) ans++;
			printf("%d\n", ans);
		}
	}
	return 0;
}

你可能感兴趣的:(图论,Tarjan,强连通分量)