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;
}
posted @ 2009-01-17 21:41  Beetlebum  阅读(304)  评论(0编辑  收藏  举报