poj 2186: Popular Cows(tarjan基础题)

题目链接

tarjan参考博客

本文代码参考博客

题意:求在图上可以被所有点到达的点的数量。

首先通过tarjan缩点,将所有内部两两可达的子图缩为一点,新图即为一个有向无环图(即DAG)。

在这个DAG上,若存在不止一个所有点均可到达的点,则所有点不满足题目要求。若存在一个,则该点所代表的连通分量的点数即为答案。

//DAG(有向无环图)上面至少存在一个出度为0的点,否则必然可以成环。

#include<cstdio>
#include<cstring>
#include<stack>
#include<vector>
using namespace std;
typedef long long LL;

const int N=10007;
int n,m;
vector<int> adj[N];
int time_tag;
int dfn[N];        // dfs序
int low[N];        // low[u]表示u及其后代所能追溯到的最早的祖先点v的dfn[v]值 
int sccno[N];
int scc_cnt; 
int size[N];    //size[i]表示编号为i的连通分量的大小
int d[N];        //d[i]==0时表示,编号为i的连通分量不认为任何其他分量popular 
stack<int> st;

void init()
{
    time_tag=scc_cnt=0;
    memset(sccno,0,sizeof(sccno));
    memset(dfn,0,sizeof(dfn));
    for(int i=1;i<=n;i++)  adj[i].clear();
    memset(d,0,sizeof(d));
}

void dfs(int u)
{
    dfn[u]=low[u]=++time_tag;
    st.push(u);
    for(int i=0;i<adj[u].size();i++)
    {
        int v=adj[u][i];
        if(!dfn[v])
        {
            dfs(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!sccno[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        int cnt=0;
        scc_cnt++;
        while(1)
        {
            int x=st.top();st.pop();
            sccno[x]=scc_cnt;
            cnt++;
            if(x==u) break; 
        }
        size[scc_cnt]=cnt;
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        init();
        for(int i=0; i<m; i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            adj[v].push_back(u);
        }
        for(int i=1; i<=n; i++)    //强连通分量缩点
            if(!dfn[i]) dfs(i);
        for(int i=1; i<=n; i++)
            for(int j=0; j<adj[i].size(); j++)
                if(sccno[i]!=sccno[adj[i][j]]) d[sccno[adj[i][j]]]++;
        int res=0;
        for(int i=1; i<=scc_cnt; i++)
            if(!d[i]) res++;
        if(res==1)
            for(int i=1; i<=scc_cnt; i++)
            {
                if(d[i]==0)
                {
                    printf("%d\n",size[i]);
                    break;
                }
            }
        else
            puts("0");
    }
}

 

转载于:https://www.cnblogs.com/Just--Do--It/p/6442136.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值