刘汝佳的书上有详细的介绍, 并且这个代码几乎和书上的是一样的
强连通就是“相互到达的关系”,每一个集合称为有向图的一个强连通分量。如果把每一个集合看成一个点,那么所有SCC构成一个SCC图,这个SCC图不会存在有向环,因此是一个DAG------------------"缩点"
此题用了个定理 :有向无环图(DAG)中,从任意一个点出发,必定可以到达某一个出度为0的点。
这个不用证明,直观想一下就行了。 因为无环,所以从一个点出发,必定会到达终点,终点的出度即是0
求强连通的目的也就是将原图改造成一个DAG,因为将属于同一强连通分支的看作一点后,这个新图就是一个DAG
我们统计DAG中出度为0的个数,如果大于1,说明出度为0的“点”不止一个,也就没有题目中要求的能够被其他所有点到达的点。(A single integer that is the number of cows who are considered popular by every other cow.)
如果出度为0的“点”(强连通分支),只有一个,那么该强连通分支中所有的点即可被剩下的点到达。该分支点的个数即是答案。
代码:
#include <iostream> #include <cstdio> #include <stack> #include <vector> #include <cstring> #define maxn 10001 using namespace std; vector<int> G[maxn]; int n, m, scc_num[maxn]; bool out[maxn]; int pre[maxn], sccno[maxn], dfs_clock, scc_cnt; stack<int> S; int dfs( int u ) { int lowu = pre[u] = ++dfs_clock; S.push(u); for( int i = 0; i < G[u].size(); i++ ) { int v = G[u][i]; if( !pre[v] ) { int lowv = dfs(v); lowu = min( lowu, lowv ); } else if( !sccno[v] ) { lowu = min( lowu,pre[v] ); } } if( lowu == pre[u] ) { scc_cnt++; while( 1 ) { int x = S.top(); S.pop(); sccno[x] = scc_cnt; scc_num[scc_cnt]++; if(x == u) break; } } return lowu; } int find_scc() { dfs_clock = scc_cnt = 0; memset(sccno,0,sizeof(sccno)); memset(pre,0,sizeof(pre)); memset(scc_num,0,sizeof(scc_num)); memset(out,0,sizeof(out)); for( int i = 0; i < n; i++ ) if( !pre[i] ) dfs(i); for( int u = 0; u < n; u++ ) { for( int i = 0; i < G[u].size(); i++ ) { int v = G[u][i]; if( sccno[u] != sccno[v] ) out[ sccno[u] ] = 1; } } bool flag = 0; int t = -1; for( int i = 1; i <= scc_cnt; i++ ) if( !flag && !out[i] ) { flag = 1; t = i; } else if( flag && !out[i] ) return 0; if( flag ) return scc_num[t]; return 0; } int main() { while( ~scanf( "%d%d", &n, &m ) ) { for( int i = 0; 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); } printf( "%d\n", find_scc() ); } return 0; }