POJ 2762 强连通分量 Kosaraju + 缩点

POJ 2762

题目链接:

http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=12633

题意:

给一个有向图,问两个点之间是否可达(能从一个点到另一个点就行)

思路:

强连通后缩点,然后判断它是不是链。

判断链的方法有很多,数据也比较水,丑陋的姿势如我能过。

源码:

#include <cstdio>

#include <cmath>

#include <cstring>

#include <cstdlib>

#include <algorithm>

#include <string>

#include <iostream>

#include <stack>

#include <vector>

#include <queue>

using namespace std;

const int MAXN = 1e3 + 5;

vector<int>lin[MAXN], G[MAXN];

int vis[MAXN];

stack<int>S;

int n, m;

int sccno[MAXN], scc_cnt;

int in[MAXN];

int lv[MAXN][MAXN];

void dfs1(int u)

{

    if(vis[u])

        return;

    vis[u] = 1;

    for(int i = 0 ; i < (int)lin[u].size() ; i++){

        int v = lin[u][i];

        if(vis[v])  continue;

        dfs1(v);

    }

    S.push(u);

}

void dfs2(int u)

{

    if(sccno[u])    return;

    sccno[u] = scc_cnt;

    for(int i = 0 ; i < (int)G[u].size() ; i++){

        int v = G[u][i];

        if(sccno[v])    continue;

        dfs2(v);

    }

}

void kosaraju()

{

    while(!S.empty())   S.pop();

    memset(vis, 0, sizeof(vis));

    for(int i = 1 ; i <= n ; i++)

        if(vis[i] == 0) dfs1(i);

    memset(sccno, 0, sizeof(sccno));

    scc_cnt = 0;

    while(!S.empty()){

        int org = S.top();  S.pop();

        if(sccno[org] == 0) scc_cnt++, dfs2(org);

    }

}

bool check()

{

    int ss = 0;

    int u = -1;

    for(int i = 1 ; i <= scc_cnt ; i++){

        if(in[i] == 0){

//            printf("i = %d\n")

            if(u == -1) u = i;

            else    return false;

        }

    }

    ss++;

//    printf("scc_cnt = %d\n", scc_cnt);

//    printf("u = %d\n", u);

//    if(u == -1) return false;

    while(vis[u] == 0){

        vis[u] = 1;

        int v = -1;

        for(int i = 1 ; i <= scc_cnt ; i++){

            if(lv[u][i])    in[i]--;

            if(in[i] == 0 && vis[i] == 0){

                if(v == -1) v = i;

                else    return false;

            }

        }

        if(v == -1)

            break;

        ss++;

        u = v;

    }

    if(ss == scc_cnt)

        return true;

    return false;

}

int main()

{

    int t;

    scanf("%d", &t);

    while(t--){

        scanf("%d%d", &n, &m);

        for(int i = 1 ; i <= n ; i++)

            lin[i].clear(), G[i].clear();

        int u, v;

        for(int i = 1 ; i <= m ; i++){

            scanf("%d%d", &u, &v);

            lin[u].push_back(v);

            G[v].push_back(u);

        }

        kosaraju();

        memset(lv, 0, sizeof(lv));

        memset(in, 0, sizeof(in));

        for(int i = 1 ; i <= n ; i++){

            for(int j = 0 ; j < (int)lin[i].size() ; j++){

                int u = i, v = lin[i][j];

                if(sccno[u] != sccno[v] && lv[sccno[u]][sccno[v]] == 0){

                    lv[sccno[u]][sccno[v]] = 1;

                    in[sccno[v]]++;

                }

            }

        }

//        printf("scc_cnt = %d\n", scc_cnt);

        int ok = 1;

        memset(vis, 0, sizeof(vis));

        ok = check();

        if(!ok)

            printf("No\n");

        else

            printf("Yes\n");

    }

    return 0;

}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值