POJ2186 Popular Cows (求所有点直接或间接指向的点)

题目链接

http://poj.org/problem?id=2186

题目

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.

题意

给定一个有向图,有边(u,v)表示牛u认为牛v是受欢迎的。同时有边(v,w)表示牛v认为牛w受欢迎,牛u认为牛w受欢迎。求出被其他所有牛认为是受欢迎的牛的总数。

分析

求出图的所有强连通分量,对分量缩点并求出缩点的大小(缩点内包含的结点数)。求出每一个缩点的出度。
如果出度为0的缩点有且只有一个,答案就是该缩点的大小。
否则,答案为0.

AC代码

//563ms 3MB
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <stack>
typedef long long ll;
using namespace std;

const int maxn=2e4+100;
vector<int> g[maxn],G[maxn];
stack<int> sta;
int n,m,cnt,dep;
int low[maxn],dfn[maxn],insta[maxn];
int par[maxn];
int sz[maxn];//sz[i]表示强连通分量i内含结点数

void tarjan(int u)//dfs确定时间戳,根据时间戳判断强连通分量
{
    low[u]=dfn[u]=++dep;//打时间戳
    sta.push(u);//入栈
    insta[u]=1;//标记在栈中
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        if(!dfn[v])//未访问过
        {
            tarjan(v);//dfs
            low[u]=min(low[u],low[v]);//更新low
        }
        else if(insta[v])//访问过且还在栈里面
        {
            //注意必须还在栈里
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(dfn[u]==low[u])//遍历完整个深搜树后u的dfn值等于low值,则它是该深搜子树的根
    {
        //从栈顶到u的结点组成一个强连通分量
        ++cnt;//强连通分量数
        while(!sta.empty())
        {
            sz[cnt]++;
            int tmp=sta.top();sta.pop();
            insta[tmp]=0;
            par[tmp]=cnt;
            if(tmp==u) break;
        }
    }
}
void init()
{
    cnt=0;//强连通分量编号
    dep=0;//结点深度
    memset(sz,0,sizeof(sz));
    memset(insta,0,sizeof(insta));
    for(int i=0;i<=n;i++) par[i]=i;
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    for(int i=0;i<=n;i++) g[i].clear();
}
int in[maxn],out[maxn];
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        init();
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            g[u].push_back(v);
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i]) tarjan(i);
        memset(in,0,sizeof(in));
        memset(out,0,sizeof(out));
        for(int i=1;i<=n;i++)//确定缩点的入度和出度
            for(int j=0;j<g[i].size();j++)
        {
            int u=i;
            int v=g[i][j];
            if(par[u]==par[v]) continue;
            in[par[v]]++;
            out[par[u]]++;
        }
        int ans,sum_out=0;
        for(int i=1;i<=cnt;i++)
        {
            if(!out[i])//入度为0才可能是答案
            {
                sum_out++;
                ans=sz[i];
            }
            if(sum_out>=2)//0入度缩点只能有一个
            {
                ans=0;
                break;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37685156/article/details/80488872