POJ 2186 强连通分量kosataju + 缩点

POJ 2186

题目链接:

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

题意:

给一个有向图,问图上所有点都能到它的点有多少个。

思路:

1A好开森。

求强连通分量,练习kosaraju。最后缩个点然后topo一下结束。

Kosaraju原理:先按照原图dfstopo一次,然后在根据反topodfs找强连通分量,和双连通分量类似。因为不会有类似割点的点同时属于两个强连通分量,所以只记录一次某点是属于哪个强连通分量即可。

源码:

#include <cstdio>

#include <cmath>

#include <cstring>

#include <cstdlib>

#include <algorithm>

#include <iostream>

#include <stack>

#include <queue>

using namespace std;

const int MAXN = 10000 + 5;

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

stack<int>sta;

int clock, vis[MAXN];

int sccno[MAXN], scc_cnt;

int n, m;

int num[MAXN];

int in[MAXN];

int sra[MAXN];

queue<int>que;

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);

    }

    sta.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()

{

    scc_cnt = clock = 0;

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

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

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

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

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

    while(!sta.empty()){

        int to = sta.top();

        sta.pop();

        if(sccno[to])   continue;

        scc_cnt++;

        dfs2(to);

    }

}

int main()

{

    while(scanf("%d%d", &n, &m) != EOF){

        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(sra, 0, sizeof(sra));

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

            sra[sccno[i]]++;

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

            shrink[i].clear();

        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]){

                    shrink[sccno[u]].push_back(sccno[v]);

                    in[sccno[v]]++;

                }

            }

        }

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

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

            num[i] = 1;

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

            if(in[i] == 0)

                que.push(i);

        }

        while(!que.empty()){

            int org = que.front();  que.pop();

            for(int i = 0 ; i < (int)shrink[org].size() ; i++){

                int v = shrink[org][i];

                in[v]--;

                num[v] += num[org];

                if(in[v] == 0)

                    que.push(v);

            }

        }

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

//            printf("i = %d, sra[%d] = %d\n", i, i, sra[i]);

//        }

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

//            printf("sccno[%d] = %d\n", i, sccno[i]);

//        }

//        printf("shrink\n");

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

//            printf("sccno = %d  shrink = ", i);

//            for(int j = 0 ; j < (int)shrink[i].size() ; j++)

//                printf("%d ", shrink[i][j]);

//            printf("\n");

//        }

//        printf("shrink\n");

        int ans = 0;

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

            if(num[i] == scc_cnt)   ans += sra[i];/// printf("i = %d\n", i);

        printf("%d\n", ans);

    }

    return 0;

}

 

你可能感兴趣的:(POJ 2186 强连通分量kosataju + 缩点)