poj 2186 Popular Cows (tarjan + 缩点)

题意 :n头奶牛,m组关系(a b代表a认为b受欢迎)。求最后有多少头奶牛所有奶牛都认为它受欢迎。

思路:首先用tarjan算出连通分支数,将所有点都标上所属连通分支color。将每个分支看做一个点,若某点a受到另一点b联系,则说明该点a代表的连通分支中每个原始点都受到b中以及所有认为b受欢迎的点的欢迎,所以此处用dfs计算出每个分支中被多少人欢迎。


代码(比较繁琐):

#include<iostream>
#include<string.h>
#define size 10010
using namespace std;
struct E
{
     int v, next;
}e[size*5],e1[size*5];
int head[size],in[size],stack[size] ,head1[size];
int dfn[size], low[size];
int num[size] ,rd[size], lian[size]; 
int top ,top1 , s ,color , step ,sum;
int n,m;

void init()
{
    for(int i=0;i<size;i++)
    {
        head[i]=head1[i]=-1;
        in[i]=stack[i]=dfn[i]=low[i]=num[i]=lian[i]=0;
    }
    step=top=top1=color=0;  s=-1;
}
void insert(int u , int v)
{
    e[top].v=v;  e[top].next=head[u];  head[u]=top++;
}
void insert1(int u , int v)
{
    e1[top1].v=v;  e1[top1].next=head1[u];  head1[u]=top1++;
} 
void tarjan(int u)
{
    int v;
    dfn[u]=low[u]=++step;
    in[u]=1;  
    stack[++s]=u;
    for(int i=head[u];i!=-1;i=e[i].next)
    {
        v=e[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            if(low[v]<low[u]) low[u]=low[v];
        }
        else if(in[v] && dfn[v]<low[u])  low[u]=dfn[v];
    }
    if(dfn[u]==low[u])
    {
        color++;   int c=0;          //  cout<<endl<<"color "<<color<<" :  "<<endl;
        do
        {
            v=stack[s--];   
            in[v]=0;
            lian[v]=color;                 //cout<<v<<"  ";
            c++;
        }while(u!=v);
        num[color]=c;
    }
} 
void dfs(int u)//连通分支u受到(xn-->...-->x1-->v-->u)所有v以及xi分支中所有原始点的欢迎。 
{
     for(int i=head1[u];i!=-1;i=e1[i].next)
     {
         int v=e1[i].v;
         if( !low[ v])
         {                         // cout<<u<<"-------"<<v<<endl;
            low[v]=1;  dfs(v);
            sum+=num[v];
         } 
     }
}

int countRd()
{
    int max=0 , t=0;    //cout<<"color= "<<color<<endl; 
     for(int i=1;i<=n;i++)
    {
        for(int j=head[i]; j!=-1; j=e[j].next)
        {
           int v=e[j].v; // cout<<i<<"************"<<v<<endl;
           if(lian[i]!=lian[v])
           {
               insert1(lian[v],lian[i]);  //用e1记录每个连通分支间的关系。 
           }
        }
    } 
    for(int i=1;i<=color;i++) 
    {          
        rd[i]=0; //cout<<"rd["<<i<<"]=****= "<<rd[i]<<endl;
        sum=rd[i];  for(int j=1;j<=color;j++)  low[j]=0;
        dfs(i);
        rd[i]=sum+num[i] ;  // cout<<"rd["<<i<<"]= "<<rd[i]<<endl;
    } 
    for(int i=1;i<=color;i++)
    {
        if(rd[i]==n)  t+=num[i]; 
    } 
    return t;
}
int main()
{
    int a, b;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
         init();
   
         for(int i=0;i<m;i++)
         {
             scanf("%d%d",&a,&b);
             if(a!=b)
             insert(a, b); 
         }
         for(int i=1;i<=n;i++)
         if(!dfn[i])  tarjan(i);      // cout<<"n= "<<n<<endl; 
         printf("%d\n",countRd());
    }
    return 0;
}

测试数据:

input
3 3
1 2
2 3
3 1

3 3
1 2
2 1
2 3

5 4
1 4
2 4
3 4
5 4

5 5
1 2
2 3
3 1
1 4
4 5

5 6
1 2
2 3
3 1
1 4
4 5
5 3

2 2
1 2
2 1

3 2
1 2
2 1

6 6
1 2
2 3
3 1
1 4
4 5
5 3

5 6
1 2
2 3
3 1
1 4
4 5
5 4

5 7
4 1
1 2
2 3
3 1
1 4
4 5
5 4

5 6
1 2
2 3
3 1
1 4
4 5	
5 1

7 9
1 2
2 3
3 1
4 5
5 6
6 4
4 7
7 1
1 7

6 6
1 2
2 3
3 1
4 5
5 6
6 4

4 4
1 2
2 3
3 1
1 4

4 4
1 2
2 3
3 1
4 1

5 6
1 2
2 3
3 1
5 1
5 4
3 4

7 9
1 2
2 3
3 1
5 1
5 4
3 4
4 7
7 6
6 4

output
3
1
1
1
5
2
0
0
2
5
5
4
0
1
3
1
3



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值