受欢迎的牛——Tarjan

每一头牛的愿望就是变成一头最受欢迎的牛。现在有 头牛,给你 对整数 ,表示牛 认为牛 受欢迎。这种关系是具有传递性的,如果 认为 受欢迎, 认为 受欢迎,那么牛 也认为牛 受欢迎。你的任务是求出有多少头牛被除自己之外的所有牛认为是受欢迎的。

输入格式
第一行两个数 ;
接下来 行,每行两个数 ,意思是 认为 是受欢迎的(给出的信息有可能重复,即有可能出现多个 )。

输出格式
输出被除自己之外的所有牛认为是受欢迎的牛的数量。

样例
输入
3 3
1 2
2 1
2 3
输出
1
只有第三头牛被除自己之外的所有牛认为是受欢迎的。

这道题写了好几天,前几天每天就是只看了半个小时,今天下午下定决心看这个题 终于把题意,算法,各数组弄清楚了

先用tarjan算法处理所有的强连通分量块出来,同时计算每个强连通块中点的个数。然后枚举所以的边,如果一条边的两个结点属于不同的强连通块,那么就把发出这条边的联通块的出度+1,最后寻找是否存在出度为0的强连通块,因为受所有牛的欢迎(在一个强联通块中的牛只要有一个欢迎另一头不再强连通块中的牛,那么强连通块中其他牛就欢迎)所以一定所以的强连通块都有边连向这个强联通块。
sc是强连通块的块数
scc表示不同的强连通块,sz表示强连通块中的点数

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stack>
#define mem(a,b) memset(a,b,sizeof(a))
const int N=2020;
typedef long long ll;
using namespace std;
struct node
{
    int to,net;
} e[N];
int head[N],tot;
void add(int u,int v)
{
    e[++tot].to=v;
    e[tot].net=head[u];
    head[u]=tot;
}
int dfn[N],low[N],cnt,s[N],tp;
int scc[N],sc;
int sz[N],n,m,out[N];
void tarjan(int u)
{
    low[u]=dfn[u]=++cnt;
    s[++tp]=u;
    for(int i=head[u]; i; i=e[i].net)
    {
        int v=e[i].to;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!scc[v])
            low[u]=min(low[u],low[v]);
    }
    if(dfn[u]==low[u])
    {
        ++sc;
        while(s[tp]!=u)
            scc[s[tp]]=sc,sz[sc]++,--tp;
        scc[s[tp]]=sc,sz[sc]++,--tp;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1; i<=m; i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
    }
    for(int i=1; i<=n; i++)
    {
        if(!dfn[i])
            tarjan(i);
    }
    for(int u=1; u<=n; u++)
    {
        for(int i=head[u]; i; i=e[i].net)
        {
            int v=e[i].to;
            if(scc[u]==scc[v])   continue;
            out[scc[u]]++;
        }
    }
    int ans=0;
    for(int i=1; i<=sc; i++)
    {
        if(!out[i])
        {
            if(!ans)  ans=i;
            else
            {
                printf("0\n");
                return 0;
            }
        }
    }
    printf("%d\n",sz[ans]);
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值