Tarjan(强连通分量缩点) - Popular Cows - POJ 2186

Tarjan(强连通分量缩点) - Popular Cows - POJ 2186

题意:

每一头牛的愿望就是变成一头最受欢迎的牛。

现在有 N 头牛,编号从 1 到 N,给你 M 对整数 (A,B),表示牛 A 认为牛 B 受欢迎。

这种关系是具有传递性的,如果 A 认为 B 受欢迎,B 认为 C 受欢迎,那么牛 A 也认为牛 C 受欢迎。

你的任务是求出有多少头牛被除自己之外的所有牛认为是受欢迎的。

输入格式

第一行两个数 N,M;

接下来 M 行,每行两个数 A,B,意思是 A 认为 B 是受欢迎的(给出的信息有可能重复,即有可能出现多个 A,B)。

输出格式

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

数据范围

1 ≤ N ≤ 1 0 4 , 1 ≤ M ≤ 5 × 1 0 4 1≤N≤10^4, 1≤M≤5×10^4 1N104,1M5×104

输入样例:

3 3
1 2
2 1
2 3

输出样例:

1

分析:

分 析 题 意 , 我 们 计 算 找 出 度 为 0 的 点 的 数 量 。 分析题意,我们计算找出度为0的点的数量。 0

最 暴 力 的 做 法 : 反 向 建 图 , 接 着 从 每 个 点 开 始 搜 索 , 看 能 否 遍 历 剩 下 的 所 有 点 , 时 间 复 杂 度 是 O ( n × ( n + m ) ) 。 最暴力的做法:反向建图,接着从每个点开始搜索,看能否遍历剩下的所有点,时间复杂度是O(n×(n+m))。 :O(n×(n+m))

可 以 通 过 t a r j a n 算 法 对 整 个 图 求 强 连 通 分 量 进 行 缩 点 , 得 到 一 个 拓 扑 图 。 可以通过tarjan算法对整个图求强连通分量进行缩点,得到一个拓扑图。 tarjan

对 于 新 的 拓 扑 图 而 言 , 统 计 所 有 强 连 通 分 量 的 出 度 , 若 出 度 为 0 , 则 该 联 通 块 内 的 所 有 点 均 符 合 条 件 。 对于新的拓扑图而言,统计所有强连通分量的出度,若出度为0,则该联通块内的所有点均符合条件。 0

注意:

若 出 度 为 0 的 强 连 通 分 量 的 数 量 大 于 1 , 则 这 些 出 度 为 0 的 强 连 通 分 量 之 间 无 法 连 通 。 即 不 存 在 符 合 条 件 的 点 。 若出度为0的强连通分量的数量大于1,则这些出度为0的强连通分量之间无法连通。即不存在符合条件的点。 010

故 仅 当 出 度 为 0 的 强 连 通 分 量 的 数 量 等 于 1 时 , 才 存 在 符 合 条 件 的 点 。 故仅当出度为0的强连通分量的数量等于1时,才存在符合条件的点。 01

点 的 数 量 即 该 强 连 通 分 量 中 点 的 数 量 。 点的数量即该强连通分量中点的数量。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=10010, M=50010;

int n,m;
int e[M],ne[M],h[N],idx;
int stk[N],top;
bool in_stk[N];
int id[N],ssc_cnt;
int dfn[N],low[N],timestamp;
int dout[N],Size[N];

void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void tarjan(int u)
{
    dfn[u]=low[u]=++timestamp;
    stk[++top]=u,in_stk[u]=true;
    for(int i=h[u];~i;i=ne[i])
    {
        int j=e[i];
        if(!dfn[j])
        {
            tarjan(j);
            low[u]=min(low[j],low[u]);
        }
        else if(in_stk[j]) low[u]=min(dfn[j],low[u]);
    }
    
    if(dfn[u]==low[u])
    {
        ++ssc_cnt;
        int y;
        do
        {
            y=stk[top--];
            in_stk[y]=false;
            id[y]=ssc_cnt;
            Size[ssc_cnt]++;
        }while(y!=u);
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    
    memset(h,-1,sizeof h);
    int a,b;
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&a,&b);
        add(a,b);
    }
    
    for(int i=1;i<=n;i++)
        if(!dfn[i])
            tarjan(i);
    
    //缩点后的图的出度统计
    for(int i=1;i<=n;i++)
        for(int j=h[i];~j;j=ne[j])
        {
            int k=e[j];
            int a=id[i],b=id[k];
            if(a!=b) dout[a]++;
        }
        
    int zeros=0,sum=0;//出度为0的强连通快数量和这些连通块中的点的总数
    for(int i=1;i<=ssc_cnt;i++)
        if(!dout[i])
        {
            zeros++;
            sum+=Size[i];
            if(zeros>1)
            {
                sum=0;
                break;
            }
        }
        
    printf("%d\n",sum);
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值