poj 2186 Popular Cows (有向图的联通分量问题)

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


题目大意:

有n只牛,牛A认为牛B很牛,牛B认为牛C很牛。给你M个关系(谁认为谁牛),求大家都认为它很牛的牛有几只。PS:如果牛A认为牛B很牛,牛B认为牛C很牛。那么我们就认为牛A认为牛C很牛。


ps:用到求有向图的强连通分量的知识和缩点的知识,不会的进http://www.cppblog.com/sosi/archive/2010/09/26/127797.aspx我感觉这个讲的很好,通俗易懂。


用tarjan算法dfs后会得到一棵树(因为是有向图,要把每个未访问的节点作为根节点进行dfs),用上面博客里介绍的方法求出连通分量,然后缩点,在对缩点进行建树(此题不用,因为只需要求缩点的出度数),缩点出度为0的联通块就是要求的答案,如果有多个出度为0的,则表示每个节点无法两两到达,也就不存在任何一点都可以到该点。当然,在计算前先判断一下是否连接每一个点(无向图的联通分量唯一)。

为什么度为0的缩点所代表的的联通块都是要求的点?

因为:A->B  B->C   那么dfs后就得到A->B->C这样的连线,此时正符合题意的传递性,假设A,B,C 代表的是所点后的,那么对于C而言出度为0,也就是说对于缩点块外的点都能到达该缩点的点,而对于每一个缩块而言,因为根据他的性质,之所以能构成缩点,是因为他们属于同一个联通分量,即在同一个强联通图中,而强联通图内的点两两互达。

注意:对于有向图,不需要判断是否为重复走的父节点,在这里调试了好久



#include <iostream>
#include <cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<vector>

using namespace std;

int arr[10005];///判断是否为一个联通分量
int low[10005],dfn[10005];///tarjan算法
int vis[10005];///标记
int svis[10005];///标记节点是否在栈内
int bcc[10005];///缩点
int top;
stack<int >s;///栈
vector<int >eage[10005];///存图
vector<int >tree[100005];///存树

int n,m;

int ant;

int f(int x)
{
    int r,y;
    r=y=x;

    while(r!=arr[r])r=arr[r];

    int t;
    while(r!=y)
    {
        t=arr[y];
        arr[y]=r;
        y=t;
    }
    return r;
}
int h=0;
void tarjan(int u)
{
    //cout<<"u "<<u<<endl;
    low[u]=dfn[u]=ant++;
    s.push(u);
    svis[u]=1;///u入栈

    vis[u]=1;

    for(int i=0; i<eage[u].size(); i++)
    {
        //cout<<"jinqu"<<endl;
        int v=eage[u][i];

       // cout<<"zheli"<<endl;
        if(!vis[v])
        {
           // cout<<"lksjdafhld"<<endl;
            tarjan(v);
           // cout<<low[v]<<" low[v]"<<endl;
           // cout<<"*low[u]  "<<u<<" "<<low[u]<<endl;
            low[u]=min(low[u],low[v]);
        }
       // cout<<"zhene"<<endl;
        //cout<<"svis[v] "<<svis[v]<<"v "<<v<<endl;
         if(svis[v])///v在栈内
        {
            //cout<<dfn[v]<<" dfn[v]"<<endl;
           // cout<<"***low[u]  "<<u<<" "<<low[u]<<endl;
            low[u]=min(low[u],dfn[v]);
        }
    }
    //cout<<"&&"<<endl;
    //cout<<"u "<<u<<" pre  ***"<<endl;
    //cout<<"dfn[u] "<<dfn[u]<<" low[u] "<<low[u]<<endl;
    if(dfn[u]==low[u])
    {
        h++;
        top++;
        while(!s.empty())
        {
            if(s.top()==u)break;
            int k=s.top();
            s.pop();
            svis[k]=0;

            bcc[k]=top;
        }
        if(!s.empty())
        {
            int k=s.top();
            s.pop();
            svis[k]=0;
            bcc[k]=top;
        }

    }
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=0; i<=n; i++)eage[i].clear(),tree[i].clear();
        while(!s.empty())s.pop();
        for(int i=0; i<=n; i++)
        {
            arr[i]=i;
            vis[i]=0;
            svis[i]=0;
            bcc[i]=i;
        }
        int x,y;

        for(int i=0; i<m; i++)
        {
            scanf("%d%d",&x,&y);
            if(f(x)!=f(y))arr[f(x)]=f(y);
            eage[x].push_back(y);
        }
        int ans=0;
        for(int i=1; i<=n; i++)if(arr[i]==i)ans++;
        if(ans!=1)
        {
            printf("0\n");
            continue;
        }
        ant=1;
        top=0;

        for(int i=1; i<=n; i++)
        {
            if(!vis[i])
            {
                tarjan(i);
            }
        }
        //cout<<"h "<<h<<endl;
        int deg[10005];
        memset(deg,0,sizeof(deg));
        for(int i=1; i<=n; i++)
        {
            for(int j=0; j<eage[i].size(); j++)
            {
                if(bcc[i]!=bcc[eage[i][j]])
                {
                    deg[bcc[i]]++;
                }
            }
        }
        memset(vis,0,sizeof(vis));

        int sum=0;
        //cout<<"top "<<top<<endl;
        for(int i=1; i<=top; i++)
        {
            if(!deg[i])
            {
                sum++;
                x=i;
            }
        }
       // cout<<sum<<" sum"<<endl;
        if(sum==1)
        {
            int ans=0;
            for(int i=1; i<=n; i++)
            {
                if(bcc[i]==x)ans++;
            }
            printf("%d\n",ans);
        }
        else printf("0\n");

    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值