poj2186

poj2186

题意

A -> B表示 A 认为 B是红人,且如果 B -> C,则 A -> C。问有几头牛被所有牛都认为是红人。

分析

如果A被所有牛认为是红人,那么A所在的强连通分量里的牛也被所有牛认为是红人。
至多只有一个强连通分量满足满足题目的条件。(否则这两个强连通分量合并,仍然是一个强连通分量)
按照Kosaraju算法 求强连通分量时,能够得到各个强连通分量拓扑排序后的顺序,而唯一能成为解的只有拓扑排序最后的连通分量,
(如果存在这样的强连通分量,那么一定会在第一个DFS中就找到(所有牛都认为它是红人),而由于rDFS逆序遍历,所以编号最大的就是那个强连通分量了。)

code

#include <iostream>
#include <cstdio>
#include <cstring>
#include<queue>
#include <algorithm>
#include <vector>

using namespace std;
const int MAXN = 1e4 + 10;
int n, m; // 点、边
int vis[MAXN];
int flag[MAXN]; // 所属强连通分量的拓扑序
vector<int> G[MAXN], rG[MAXN];
vector<int> vs; // 后序遍历顺序的顶点列表

void addedge(int x, int y)
{
    G[x].push_back(y);  // 正向图
    rG[y].push_back(x); // 反向图
}

void dfs(int u)
{
    vis[u] = 1;
    for(int i = 0; i < G[u].size(); i++)
    {
        int v = G[u][i];
        if(!vis[v]) dfs(v);
    }
    vs.push_back(u);
}

void rdfs(int u, int k)
{
    vis[u] = 1;
    flag[u] = k;
    for(int i = 0; i < rG[u].size(); i++)
    {
        int v = rG[u][i];
        if(!vis[v]) rdfs(v, k);
    }
}

int scc() // 强连通分量的个数
{
    vs.clear();
    memset(vis, 0, sizeof vis);
    for(int i = 1; i <= n; i++) // [1...n]
        if(!vis[i]) dfs(i);
    memset(vis, 0, sizeof vis);
    int k = 0;
    for(int i = vs.size() - 1; i >= 0; i--)
        if(!vis[vs[i]]) rdfs(vs[i], k++);
    return k;
}

int solve()
{
    int cnt = scc(), num = 0, u = 0;
    for(int i = 1; i <= n; i++)
        if(flag[i] == cnt - 1)
        {
            num++;
            u = i;
        }
    memset(vis, 0, sizeof vis);
    rdfs(u, 0);
    for(int i = 1; i <= n; i++)
        if(!vis[i]) num = 0;
    return num;
}

int main()
{
    while(~scanf("%d%d", &n, &m))
    {
        for(int i = 0; i <= n; i++)
        {
            G[i].clear();
            rG[i].clear();
        }
        for(int i = 0; i < m; i++)
        {
            int x, y;
            scanf("%d%d", &x, &y);
            addedge(x, y);
        }
        printf("%d\n", solve());
    }
    return 0;
}
posted @ 2017-05-01 09:43  ftae  阅读(190)  评论(0编辑  收藏  举报