Popular Cows POJ - 2186 (Tarjan求强连通分量 + 缩点)

Every cow’s dream is to become the most popular cow in the herd. In a herd of N (1 <= N <= 10,000) cows, you are given up to M (1 <= M <= 50,000) ordered pairs of the form (A, B) that tell you that cow A thinks that cow B is popular. Since popularity is transitive, if A thinks B is popular and B thinks C is popular, then A will also think that C is
popular, even if this is not explicitly specified by an ordered pair in the input. Your task is to compute the number of cows that are considered popular by every other cow.

Input

Line 1: Two space-separated integers, N and M
Lines 2…1+M: Two space-separated numbers A and B, meaning that A thinks B is popular.

Output

Line 1: A single integer that is the number of cows who are considered popular
by every other cow.

Sample Input
3 3
1 2
2 1
2 3

Sample Output
1

Hint
Cow 3 is the only cow of high popularity.

题目意思
给出n个节点,m条边的有向图,让你求有多少节点可以从其他任意节点到达。**

思路
先求出所有的强连通分量,然后把每个强连通分量缩成一个点,这样整个图就没有强连通分量了,也就是不存在环。然后找到出度为0的点的个数,只有出度为0的点的个数为1才存在能满足题意的节点,再计算出这个出度为0的点包含几个节点答案就是几。(能满足提议的收缩之后的点出度确实必须为0,不然的话在其他所有点都能到达它的前提下,如果它出度不为0,那么意味着它可以到达其他点,构成了环,与图中不存在环相矛盾。如果出度为0的点的个数大于1的话,那么意味着这几个点之间互相不可到达,就不满足题意了。

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

using namespace std;

const int maxn = 5 * 1e4 + 100;

int n, m, top, num, tot, sum;
int head[maxn], Stack[maxn], color[maxn], degree[maxn], dfn[maxn], low[maxn];

struct node
{
    int v, next;
}edge[maxn];

inline void add(int u, int v) 
{
    edge[tot].v = v;
    edge[tot].next = head[u]; 
    head[u] = tot++;
}

inline void Init()
{
    top = 0;
    tot = 0;
    num = 0;
    sum = 0;
    memset(dfn, 0, sizeof(dfn));
    memset(head, -1, sizeof(head));
}
//链式前向星存图以及初始化

void recon(int u) {            //缩点,并统计出度
    for(int i = head[u]; i != -1; i = edge[i].next) {
        int v = edge[i].v;
        if(color[v] != color[u])
        {
            degree[color[u]] ++;
        }
    }
}

void Tarjan(int u)       //求强连通分量
{
    dfn[u] = low[u] = ++num;
    Stack[++top] = u;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].v;
        if(!dfn[v])
        {
            Tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(!color[v]) 
	        low[u] = min(low[u], dfn[v]);
    }
    if(dfn[u] == low[u])
    {
        color[u] = ++ sum;         //给这些节点安排上一个新的编号(缩点之后的编号)
        while(Stack[top] != u)
        {
            color[Stack[top--]] = sum;
        }
        top--;
    }
}


int main() 
{
    //freopen("in.txt", "r", stdin);
    cin >> n >> m;
    Init();
    int u, v;
    for(int i = 1; i <= m; ++ i) 
    {
        scanf("%d%d", &u, &v);
        add(u, v);
    }
    for(int i = 1; i <= n; ++ i)  //Tarjan求强连通分量
    {
        if(!dfn[i])
            Tarjan(i);
    }
    for(int i = 1; i <= n; ++ i)     //缩点,并统计每个点的出度
    {
        recon(i);
    }
    int cnt = 0;
    int id = -1;
    for(int i = 1; i <= sum; ++ i)       //找到出度为零的点的个数,并记录下其节点编号(缩点后的编号)
    {
        if(degree[i] == 0)
        {
            cnt ++;
            id = i;
        }
    }
    int ans = 0;
    if(cnt == 1)             //如果出度为零的点只有一个那么答案就是这一个强连通分量中的所有点
    {
        for(int i = 1;i <= n; ++ i) 
        {
            if(color[i] == id)
                ans++;
        }
    }
    printf("%d\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/aqa2037299560/article/details/86155858
今日推荐