pku1236 Network of Schools

求强连通分量的基础题,用来练一下刚搞懂的Tarjan:

Tarjan算法的过程就是不断避免把桥纳入强连通分量中

注意到以下性质:
1,桥一定是DFS树中的边
2,一条树边v-w为桥,当且仅当不存在回边将w的一个子孙与w的一个祖先相连

 

#include  < iostream >
#include 
< stack >
using   namespace  std;

#define  Max(a,b)(a<b?b:a)
#define  MAXN 101

int  p[MAXN],ecnt,n,c,ideg[MAXN],odeg[MAXN];
int  lowlink[MAXN], // 结点所处强连通分量的代表结点,也是具有最小时间截的结点(在DFS树中深度最浅)(动态更新)
    dfn[MAXN], // 访问时间(固定)
    sign, // 时间截
    SCC[MAXN]; // 结点所处强连通分量
bool  visited[MAXN],instack[MAXN];
stack
< int >  st;

struct  Edge{
    
int  v,next;
}edg[
10000 ];

void  update( int   & a, int  b){
    a
= (a < b ? a:b);
}

void  dfs( int  u){
    
int  i,v;
    st.push(u);
    visited[u]
= instack[u] = true ;
    lowlink[u]
= dfn[u] =++ sign;
    
for (i = p[u];i !=- 1 ;i = edg[i].next){
        v
= edg[i].v;
        
if (visited[v]){
            
if (instack[v]){
                update(lowlink[u],bfn[v]);
// u-v是返祖边
            }
        }
        
else {
            dfs(v);
            update(lowlink[u],lowlink[v]);
        }
    }
    
if (lowlink[u] == dfn[u]){ // 若u是代表结点,则深度比其深的仍在栈内的待处理结点均属于该强连通分量
        c ++ ;
        
do {
            v
= st.top();
            st.pop();
            instack[v]
= false ;
            SCC[v]
= c;
        }
while (v != u);
    }
}

void  Tarjan(){
    
int  i;
    c
= 0 ;
    sign
= 0 ;
    memset(visited,
false , sizeof (visited));
    memset(instack,
false , sizeof (instack));
    
for (i = 1 ;i <= n;i ++ )
        
if ( ! visited[i])
            dfs(i);
}

void  solve(){
    
int  i,u,v,ic,oc;

    Tarjan();
// 求强连通分量
    

    memset(ideg,
0 , sizeof (ideg));
    memset(odeg,
0 , sizeof (odeg));
    
for (u = 1 ;u <= n;u ++ ){
        
for (i = p[u];i !=- 1 ;i = edg[i].next){
            v
= edg[i].v;
            
if (SCC[u] != SCC[v]){
                odeg[SCC[u]]
++ ;
                ideg[SCC[v]]
++ ;
            }
        }
    }

    
if (c == 1 ){
        printf(
" 1\n0\n " );
        
return ;
    }


    ic
= oc = 0 ;
    
for (i = 1 ;i <= c;i ++ ){
        
if (ideg[i] == 0 )
            ic
++ ;
        
if (odeg[i] == 0 )
            oc
++ ;
    }
    printf(
" %d\n%d\n " ,ic,Max(ic,oc));
}


int  main(){
    
int  u,v;
    
while (scanf( " %d " , & n) != EOF){
        
while ( ! st.empty())
            st.pop();
        ecnt
= 0 ;
        memset(p,
- 1 , sizeof (p));
        
for (u = 1 ;u <= n;u ++ ){
            
while (scanf( " %d " , & v)  &&  v){
                edg[ecnt].v
= v;
                edg[ecnt].next
= p[u];
                p[u]
= ecnt ++ ;
            }
        }
        solve();
    }
    
return   0 ;
}

转载于:https://www.cnblogs.com/zgmf_x20a/archive/2009/01/18/1377892.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值