poj - 2186- 强连通 - 最受欢迎的牛的个数

来源:互联网 发布:数据库最大连接数 编辑:程序博客网 时间:2024/05/10 08:43

刘汝佳的书上有详细的介绍, 并且这个代码几乎和书上的是一样的

强连通就是“相互到达的关系”,每一个集合称为有向图的一个强连通分量。如果把每一个集合看成一个点,那么所有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 10001using 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;}