首页 > 代码库 > POJ2186 Popular Cows ,有向图, Tarjan算法

POJ2186 Popular Cows ,有向图, Tarjan算法

题意:

给定一个有向图,求有多少个顶点是由任何顶点出发都可达的。

顶点数<= 10,000,边数 <= 50,000


定理:
有向无环图中唯一出度为0的点,一定可以由任何点出发均可达
(由于无环,所以从任何点出发往前走,必然终止于一个出度为0的点)



1. 求出所有强连通分量
2. 每个强连通分量缩成一点,则形成一个有向无环图DAG。
3. DAG上面如果有唯一的出度为0的点,则该点能被所有的点可达。那么该点所代表的连通分量上的所有的原图中的点,都能被原图中的所有点可达,则该连通分量的点数,就是答案。
4. DAG上面如果有不止一个出度为0的点,则这些点互相不可达,原问题无解,答案为0


#include <cstdio>
#include <cstring>
#include <vector>
#include <stack>
#include <iostream>
#include <algorithm>
using namespace std;

const int maxn = 10000 + 100;
vector<int> g[maxn];
int dfn[maxn], low[maxn], belong[maxn], dfs_clock, scc_cnt, size[maxn];
stack<int> s;
int n, m;

void dfs(int u){  
    dfn[u] = low[u] = ++dfs_clock;  
    s.push(u);  
    for(int i=0; i<g[u].size(); ++i){  
        int v = g[u][i];  
        if(!dfn[v]){  
            dfs(v);  
            low[u] = min(low[u], low[v]);  
        }else if(!belong[v]){  
            low[u] = min(low[u], dfn[v]);  
        }  
    }  
    if(low[u] == dfn[u]){  
        scc_cnt++;  
        for(;;){  
            int x = s.top(); s.pop();  
            belong[x] = scc_cnt;  
			size[scc_cnt]++;
            if(x == u) break;  
        }  
    }  
}  
  
void find_scc(int n){  
    dfs_clock = scc_cnt = 0;  
    memset(belong, 0, sizeof belong );  
    memset(dfn, 0, sizeof dfn );  
    for(int i=1; i<=n; ++i)  
        if(!dfn[i]) dfs(i);  
}  

int main(){
	scanf("%d%d", &n, &m);
	int u, v;
	for(int i=0; i<m; ++i){
		scanf("%d%d", &u, &v);
		g[u].push_back(v);
	}
	find_scc(n);
	
	int out[maxn];
	memset(out, 0, sizeof out );
	for(int i=1; i<=n; ++i){
		for(int j=0; j<g[i].size(); ++j){
			int &v = g[i][j];
			if(belong[i] != belong[v]){
				out[belong[i]]++;
			}
		}
	}	
	
	int cnt = 0, ans;
	for(int i=1; i<=scc_cnt; ++i){
		if(out[i]==0){
			cnt++;
			ans = size[i];
		}
	}
	if(cnt==1){
		printf("%d\n", ans);
	}else {
		printf("0\n");
	}	

	return 0;
}


POJ2186 Popular Cows ,有向图, Tarjan算法