POJ2186 Popular Cows 题解 强连通分量入门题

题目链接:http://poj.org/problem?id=2186
题目大意:
每头牛都想成为牛群中的红人。
给定N头牛的牛群和M个有序对(A, B),(A, B)表示牛A认为牛B是红人;
该关系具有传递性,所以如果牛A认为牛B是红人,牛B认为牛C是红人,那么牛A也认为牛C是红人。
不过,给定的有序对中可能包含(A, B)和(B, C),但不包含(A, C)。
求被其他所有牛认为是红人的牛的总数。

题目分析(引自 https://www.cnblogs.com/violet-acmer/p/9740737.html):
考虑以牛为顶点的有向图,对每个有序对(A, B)连一条从 A到B的有向边;
那么,被其他所有牛认为是红人的牛对应的顶点,也就是从其他所有顶点都可达的顶点。
虽然这可以通过从每个顶点出发搜索求得,但总的复杂度却是O(NM),是不可行的,必须要考虑更为高效的算法。
假设有两头牛A和B都被其他所有牛认为是红人,那么显然,A被B认为是红人,B也被A认为是红人;
即存在一个包含A、B两个顶点的圈,或者说,A、B同属于一个强连通分量。
反之,如果一头牛被其他所有牛认为是红人,那么其所属的强连通分量内的所有牛都被其他所有牛认为是红人。
由此,我们把图进行强连通分量分解后,至多有一个强连通分量满足题目的条件。
而按前面介绍的算法进行强连通分量分解时,我们还能够得到各个强连通分量拓扑排序后的顺序;
唯一可能成为解的只有拓扑序最后的强连通分量。
所以在最后,我们只要检查这个强连通分量是否从所有顶点可达就好了。

思路整理:
1、首先,使用tarjan缩点;
2、其次,检查是否所有点可达
只需要确定是不是缩点后只有一个点的出度为0即可。
如果只有一个点的出度为0,则答案为该点对应的强连通分量中的原图中的点的数量;
否则,答案为 0。

实现代码如下:

#include <iostream>
#include <vector>
#include <stack>
#include <cstring>
using namespace std;
const int maxn = 10010;
int n, dfn[maxn], low[maxn], belong[maxn], idx, cnt;
bool instk[maxn];
stack<int> stk;
vector<int> g[maxn];
void tarjan(int u) {
    dfn[u] = low[u] = ++idx;
    instk[u] = true;
    stk.push(u);
    int sz = g[u].size();
    for (int i = 0; i < sz; i ++) {
        int v = g[u][i];
        if (!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if (instk[v]) low[u] = min(low[u], dfn[v]);
    }
    if (dfn[u] == low[u]) {
        cnt ++;
        int v;
        do {
            v = stk.top();
            stk.pop();
            instk[v] = false;
            belong[v] = cnt;
        } while (u != v);
    }
}
void solve() {
    memset(dfn, 0, sizeof(dfn));
    memset(instk, 0, sizeof(instk));
    for (int i = 1; i <= n; i ++) if (!dfn[i]) tarjan(i);
}
int m;
bool vis[maxn];
int main() {
    cin >> n >> m;
    while (m --) {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
    }
    solve();
    for (int u = 1; u <= n; u ++) {
        int sz = g[u].size();
        for (int i = 0; i < sz; i ++) {
            int v = g[u][i];
            if (belong[u] != belong[v]) {
                vis[ belong[u] ] = true;
            }
        }
    }
    int cc = 0, id = -1;
    for (int i = 1; i <= cnt; i ++) if (!vis[i]) {
        cc ++;
        id = i;
    }
    if (cc != 1) {
        cout << 0 << endl;
        return 0;
    }
    int ans = 0;
    for (int i = 1; i <= n; i ++) if (belong[i] == id) ans ++;
    cout << ans << endl;
    return 0;
}
posted @ 2019-11-09 16:28  codedecision  阅读(205)  评论(0编辑  收藏  举报