【代码】POJ 2762

// 题目来源:POJ 2762
// 题目大意:给定一个有向图,问该图是否弱连通(若图中任意两点之间可以到达,即可以i到j或者j到i,该图即为弱连通)
// 解决方法:强连通分量缩点,当且仅当该图的拓扑序列唯一的时候才成立,那么只需记录当前入度为0的个数,当多于1个时不成立

#include <cstdio>
#include <string>
#define _ 60002
#define o 10002
using namespace std;

void link( int, int );
void link2( int, int );
void tarjan( int );
bool judge( );
void topsort( int );

int dfn[ o ], low[ o ], h[ o ], h2[ o ], id[ o ], od[ o ], stack[ o ], code[ o ];
int next[ _ ], g[ _ ], next2[ _ ], g2[ _ ];
int q, n, m, t, t2, top, cnt, index, sid;
bool ins[ o ];
bool flag;

int main( )
{
    freopen( "2762.in", "r", stdin );
    freopen( "2762.out", "w", stdout );
    scanf( "%d", &q );
    int aa, bb;
    while( q-- )
    {
        memset( dfn, 0, sizeof( dfn ) );
        memset( low, 0, sizeof( low ) );
        memset( next, 0, sizeof( next ) );
        memset( h, 0, sizeof( h ) );
        memset( next2, 0, sizeof( next2 ) );
        memset( h2, 0, sizeof( h2 ) );
        memset( id, 0, sizeof( id ) );
        memset( od, 0, sizeof( od ) );
        memset( code, 0, sizeof( code ) );
        t = 0;
        t2 = 0;
        cnt = 0;
        scanf( "%d%d", &n, &m );
        for( int i = 1; i <= m; i++ )
        {
            scanf( "%d%d", &aa, &bb );
            link( aa, bb );
        }
        for( int i = 1; i <= n; i++ )
            if( !dfn[ i ] ) tarjan( i );
        if( judge( ) )
            printf( "Yes\n" );
        else
            printf( "No\n" );
    }
    return 0;
}

void link( int aa, int bb )
{
    next[ ++t ] = h[ aa ];
    h[ aa ] = t;
    g[ t ] = bb;
}

void link2( int aa, int bb )
{
    next2[ ++t2 ] = h2[ aa ];
    h2[ aa ] = t2;
    g2[ t2 ] = bb;
}

void tarjan( int i )
{
    int j;
    dfn[ i ] = low[ i ] = ++index;
    stack[ ++top ] = i;
    ins[ i ] = true;
    for( int k = h[ i ]; k; k = next[ k ] )
    {
        j = g[ k ];
        if( !dfn[ j ] )
        {
            tarjan( j );
            if( low[ j ] < low[ i ] ) low[ i ] = low[ j ];
        }
        else if( ins[ j ] && dfn[ j ] < low[ i ] )
            low[ i ] = dfn[ j ];
    }
    if( low[ i ] == dfn[ i ] )
    {
        cnt++;
        do
        {
            j = stack[ top-- ];
            code[ j ] = cnt;
            ins[ j ] = false;
        }
        while( i != j );
    }
}

bool judge( )
{
    int j;
    flag = true;
    sid = 0;
    for( int i = 1; i <= n; i++ )
        for( int k = h[ i ]; k; k = next[ k ] )
        {
            j = g[ k ];
            if( code[ i ] != code[ j ] )
            {
                id[ code[ j ] ]++;
                link2( code[ i ], code[ j ] );
            }
        }
    for( int i = 1; i <= cnt; i++ )
        if( id[ i ] == 0 ) sid++;
    if( sid > 1 ) return 0;
    for( int i = 1; i <= cnt; i++ )
        if( id[ i ] == 0 ) topsort( i );
    if( flag ) return 1;
    return 0;
}

void topsort( int i )
{
    int j;
    sid--;
    id[ i ] = -1;
    for( int k = h2[ i ]; k; k = next2[ k ] )
    {
        j = g2[ k ];
        id[ j ]--;
        if( id[ j ] == 0 ) sid++;
    }
    if( sid > 1 ) flag = false;
    for( int k = h2[ i ]; k; k = next2[ k ] )
    {
        j = g2[ k ];
        if( id[ j ] == 0 ) topsort( j );
    }
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值