#牛客网 HAOI2006 受欢迎的牛

思路 : 首先用tarjan缩点,那么原图就成了DAG, 判定的条件为 新的DAG图如果只有一个点 (该点可能是孤立的也可能是几个)的出度为0, 那么就将这个点的原图 (可以说是他所包含的原点的集合)中的点的数目表示出来,但是如果出度为0的点个数大于1了,则不成立,直接输出0,因为这样的话就出现两个牛群(牛群可能是一只牛也可能是几只牛),那么就无法做到强连通。

AC代码如下 :

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5; // 1e4会越界

struct node
{
    int v, next;    //链式前向星
}e[maxn]; 
int dfn[maxn], low[maxn], sum[maxn], cnt, tot, scnt; // sum记录DAG每个“点”包含原图点的个数
int head[maxn], out[maxn], suo[maxn], n, m;  //out表示出度, suo用来存缩点后的新点
bool vis[maxn];
stack <int> st;
void add (int from, int to) {
    e[++cnt].v = to;
    e[cnt].next = head[from];
    head[from] = cnt;
}
void tarjan (int x) {
    dfn[x] = low[x] = ++tot;
    vis[x] = 1;
    st.push(x);
    for (int i = head[x]; i != -1; i = e[i].next) {
        if (!dfn[e[i].v]) {
            tarjan (e[i].v);
            low[x] = min (low[x], low[e[i].v]);
        }
        else if (vis[e[i].v]) low[x] = min (low[x], dfn[e[i].v]);
    }
    if (dfn[x] == low[x]) {
        ++scnt;    // 新的DAG图的点的个数
        int k;  
        do {
            k = st.top();
            st.pop();
            vis[k] = 0;
            sum[scnt]++;    //新的DAG图包含的点的个数,每次+1
            suo[k] = scnt;   // 原图的点若在DAG的一个集合中,那么suo相等
        }
        while (k != x);
    }
}

int main()
{
    memset(head, -1, sizeof(head));
    cin >> n >> m;
    for (int i = 0; i < m; i++) {
        int ui, vi;
        cin >> ui >> vi;
        add (ui, vi);
    }
    for (int i = 1; i <= n; i++) {
        if (!dfn[i]) tarjan (i);
    }
    for (int i = 1; i <= n; i++) {
        for (int j = head[i]; j != -1; j = e[j].next) {
            int t = e[j].v;
            if (suo[i] != suo[t]) out[suo[i]]++; //原来连着,但是新DAG图中不在一起,出度+1
        }
    }
    int ans = 0, oscnt = 0;
    for (int i = 1; i <= scnt; i++) {
        if (!out[i]) ans = sum[i], oscnt++; // oscnt表示出度为0的个数,
    }
    if (oscnt > 1) cout << 0 << endl;
    else cout << ans << endl;
    return 0;
}

 

事实上,suo数组中存的新DAG点,就是按照tarjan的流程来的

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值