tarjan应用(受欢迎的牛 G 洛谷)

题目描述

每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果 AA 喜欢 BB,BB 喜欢 CC,那么 AA 也喜欢 CC。牛栏里共有 NN 头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。

输入格式
第一行:两个用空格分开的整数:NN 和 MM。

接下来 MM 行:每行两个用空格分开的整数:AA 和 BB,表示 AA 喜欢 BB。

输出格式
一行单独一个整数,表示明星奶牛的数量。

输入输出样例
输入 #1复制
3 3
1 2
2 1
2 3
输出 #1复制
1
说明/提示
只有 33 号奶牛可以做明星。

解:

首先,不难发现,如果这所有的牛都存在同一个强联通分量里。那么它们一定互相受欢迎。

那么,我们怎么来找明星呢。

很简单,找出度为00的强联通分量中的点。这样可以保证所有的人都喜欢它,但是它不喜欢任何人,所以说不存在还有人事明星。

此题还有一个特殊情况:

如果有两个点分别满足出度为零的条件,则没有明星,这样无法满足所有的牛喜欢他。

#include <iostream>
#include<cstdio>
#include<stack>
using namespace std;
#define N 10005
#define M 50005

int n,m,cnt=1,head[N],dfn[N],book[N],low[N],num,belong[N],de[N],numm[N];
struct e{
    int to;
    int nxt;
};

e edge[M];
void add(int s, int v){
    edge[cnt].to = v;
    edge[cnt].nxt = head[s];
    head[s] = cnt++;
    return;
}
int t=0;
stack <int> zhan;

void tarjan(int x){
    low[x] = dfn[x] = ++t;
    zhan.push(x);
    book[x] = 1;
    for(int i=head[x]; i ;i = edge[i].nxt){
        if(dfn[edge[i].to]==0){//不能写成book[] == 0  因为只要搜过了就不用再搜了,下方样例可以很好说明  !!!
            tarjan(edge[i].to);
            low[x] = min(low[x], low[edge[i].to]);
        }
        else if(book[edge[i].to] == 1){
            low[x] = min(low[x], dfn[edge[i].to]);
        }
    }
    if(dfn[x] == low[x]){
        num++;
        while(1){
            int a = zhan.top();
            belong[a] = num;//每一个点属于哪个强连通区域
            zhan.pop();
            book[a] = 0;
            if(a == x) break;
            
        }
    }
    return ;
}

int main() {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);
    }
    
    for(int i=1;i<=n;i++){
        if(dfn[i] == 0) tarjan(i);
    }
    for(int i=1;i<=n;i++){
        numm[belong[i]]++;
        for(int j=head[i];j;j=edge[j].nxt){
            if(belong[i]!=belong[edge[j].to]){
                de[belong[i]]++;//记录每组连通分量的出度
            }
            
        }
    }
    int rec;
    int flag=0;
    for(int i=1;i<=num;i++){//遍历所有的连通区域
        if(de[i]==0){
            rec = i;
            flag++;
        }
    }
    if(flag>1) printf("0\n");//出现一次以上出度为零的连通分量
    else
        printf("%d\n",numm[rec]);
    return 0;
}
/*
 6 8
 1 3
 1 2
 3 4
 2 4
 4 1
 3 5
 4 6
 5 6
*/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值