pku2186 Popular Cows

Kosaraju算法的原理:先对逆图作一遍后序遍历,计算访问时间f[u](如果图是一个DAG(有向无环图),这一过程产生一个拓扑排序序列)。然后在原图上再一次DFS,不过是从f[u]最大的未访问点开始遍历。

这样得到的就是一个强连通分量了。因为当原图中w点可以到达v点的时候,在访问逆图的时,逆图中的dfs树中v才可能是w的父节点,这样,f[v]将大于f[w]。再在原图中作DFS,由于是优先访问具有最高后序编号的未访问的结点,如果在原图中v不可到达w,则v和w不可能在同一棵dfs树内,那么v和w不在同一连通分支中。就这么简单的方法。

注意到按照f[u]递减的顺序dfs实际上就是按照拓扑排序的访问各顶点。

 

#include  < iostream >
using   namespace  std;

#define  MAXN 10001

struct  Edge{
    
int  v,next;
}edg[
10 * MAXN];

int  p[MAXN],p1[MAXN],n,m,pcnt,post[MAXN],postcnt,c;
int  visited[MAXN];
int  cdeg[MAXN];

void  dfs( int  u){
    
int  i,v;
    
for (i = p1[u];i !=- 1 ;i = edg[i].next){
        v
= edg[i].v;
        
if ( ! visited[v]){
            visited[v]
= 1 ;
            dfs(v);
        }
    }
    post[postcnt
++ ] = u;
}

void  dfs1( int  u){
    
int  i,v;
    
for (i = p[u];i !=- 1 ;i = edg[i].next){
        v
= edg[i].v;
        
if ( ! visited[v]){
            visited[v]
= c;
            dfs1(v);
        }
    }
}


int  main(){
    
int  i,j,u,v,cnt,ans,mark;
    
while (scanf( " %d%d " , & n, & m) != EOF){
        memset(p,
- 1 , sizeof (p));
        memset(p1,
- 1 , sizeof (p1));
        pcnt
= 0 ;
        
for (i = 0 ;i < m;i ++ ){
            scanf(
" %d%d " , & u, & v);
            edg[pcnt].next
= p[u];
            edg[pcnt].v
= v;
            p[u]
= pcnt ++ ;
            edg[pcnt].next
= p1[v];
            edg[pcnt].v
= u;
            p1[v]
= pcnt ++ ;
        }

        memset(visited,
0 , sizeof (visited)); // 反向图,求f[u]
        postcnt = 0 ;
        
for (i = 1 ;i <= n;i ++ ){
            
if ( ! visited[i]){
                visited[i]
= 1 ;
                dfs(i);
            }
        }

        c
= 0 ;
        memset(visited,
0 , sizeof (visited)); // 正向图
         for (i = postcnt - 1 ;i >= 0 ;i -- ){
            
if ( ! visited[post[i]]){
                visited[post[i]]
=++ c; // 标记各SCC
                dfs1(post[i]);
            }
        }

        memset(cdeg,
0 , sizeof (cdeg)); // 计算各SCC的出度
         for (i = 1 ;i <= n;i ++ ){
            
for (j = p[i];j !=- 1 ;j = edg[j].next){
                
if (visited[i] != visited[edg[j].v]){
                    cdeg[visited[i]]
++ ;
                }
            }
        }

        cnt
= 0 ;
        
for (i = 1 ;i <= c;i ++ ){ // 计算出度为0的SCC个数
             if (cdeg[i] == 0 ){
                cnt
++ ;
                mark
= i;
            }
        }

        
if (cnt > 1 ){
            printf(
" 0\n " );
            
continue ;
        }

        ans
= 0 ;
        
for (i = 1 ;i <= n;i ++ ){
            
if (visited[i] == mark)
                ans
++ ;
        }
        printf(
" %d\n " ,ans);
    }
    
return   0 ;
}

你可能感兴趣的:(pku)