POJ 2186 Popular Cows(强连通分量缩点)

35 篇文章 0 订阅
Popular Cows
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 36225 Accepted: 14759

Description

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. 

Source

USACO 2003 Fall


思路:最直接的想法是暴力搜索,首先,如果一个点能够到达所有点,则这头牛崇拜所有的牛,那么反过来(将所有路线反向,即崇拜变为被崇拜)之后,如果某个点能到达所有点,那个这头牛就被所有的牛崇拜(逆向思维),不过这样做的复杂度高达O(m*n),会超时。只得另辟蹊径了,首先来看有向无环图,其中有两类点非常特殊,一种是入度为0的点,一种是出度为0的点,如果某个有向无环图中只有一个出度为0点,那么所有的点都有路径到达这个点,而且有且仅有这个点(DAG(有向无环图的性质 无论图是否连通 可以自己画一下))。如果题目中这个图是有向无环图就好办了,可惜不是。。。那么是不是可以转化成有向无环图呢,关键在于如何去掉环路,每个最大的环路其实就是一个连通分量,在这个环里面所有奶牛都是互相崇拜的,不用管环内部的情况,把每个环看成一个点,重新构图,再寻找出度为0的点,,出度为0的个数一定大于等于1(当原图为连通图时,转化为1个点,个数最小为1),如果等于1,答案则是出度为0对应的连通分量的点的个数。(注意如果图中有孤立的点也会被考虑进去,即出度为0的个数大于1)。这里求强连通分量用的是tarjan算法。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stdlib.h>
#include<vector>
#include<stack>
using namespace std;
#define N 10005
stack<int> sta;
vector<int> mp[N];
int dfn[N],low[N],vis[N],num[N],degree[N];
int n,m,cnt,id,ans;

void init(){
    cnt=0;
    id=0;
    ans=0;
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    memset(num,0,sizeof(num));
    memset(degree,0,sizeof(degree));
    while(!sta.empty())
        sta.pop();
    for(int i=1;i<=n;i++)
        mp[i].clear();
}

void tarjan(int x){
    dfn[x]=low[x]=++id;
    sta.push(x);
    vis[x]=1;
    for(int i=0;i<mp[x].size();i++){
        int t=mp[x][i];
        if(!dfn[t]) {
            tarjan(t);
            low[x]=min(low[x],low[t]);
        }else if(vis[t]) low[x]=min(low[x],dfn[t]);
    }

    if(dfn[x]==low[x]){
        int tp;
        cnt++;
        do{
            tp=sta.top();
            vis[tp]=0;
            num[tp]=cnt;
            sta.pop();
        }while(tp!=x);
    }
}

void slove(){
    int sum=0,index;
    for(int i=1;i<=n;i++){
        for(int j=0;j<mp[i].size();j++){
            if(num[i]!=num[mp[i][j]])
                degree[num[i]]++;
        }
    }

    for(int i=1;i<=cnt;i++){
        if(!degree[i])
            sum++,index=i;
    }

    if(sum>1) cout<<"0"<<endl;
    else {
        for(int i=1;i<=n;i++)
            if(num[i]==index)
                ans++;
        cout<<ans<<endl;
    }
}


int main(){
    while(cin>>n>>m){
        init();
        for(int i=0;i<m;i++){
            int a,b;
            cin>>a>>b;
            mp[a].push_back(b);
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i])
                tarjan(i);
        slove();
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值