poj 2186 Popular Cows 夜

http://poj.org/problem?id=2186

给你n头牛 他们之间会有一种 A认为B popular 的关系 而且这种关系是可传递的

问你有多少头牛是被所有其他的牛认为popular

1,缩点 将一个联通分量内的点缩成一个

2,缩点同时记录联通分量内点的个数

3,缩点后图变成了树 或者是 (非树)森林 如果是树 则树根连通分量内的点就是答案 如果是森林 答案为0

    生成的树或森林 根方向上是被指向的。

4,由于3的原因 搜索时还有注意标记哪些缩点是没有可能成为根的缩点

5,如果根缩点只有一个(树) 则根就是答案 如果多个(非树森林)则为0

注意 Tarjan 搜索是并不一定一次就能将所有的点搜干净 注意这一点 想办法处理

代码及其注释:

#include<iostream>
#include<cstring>
#include<stack>
#include<cstdio>
#include<queue>

using namespace std;

const int N=10005;
struct node
{
    struct tt *next;
}mem[N];
struct tt
{
    struct tt *next;
    int j;
};
int deep;
int low[N];
int dfn[N];
bool visited[N];
bool in[N];
stack<int>str;
int uppoint[N];
int th[N];//第几次用Tarjan搜索
bool f[N];//如果此点为其它缩点的父结点 所在联通分量不可能是根
int sum[N];
void build(int i,int j)
{
    struct tt *t=new tt;
    t->j=j;
    t->next=mem[i].next;
    mem[i].next=t;
}
void Clearlist(int n)
{
    struct tt *t;
    for(int i=1;i<=n;++i)
    {
        while(mem[i].next!=NULL)
        {
            t=mem[i].next;
            mem[i].next=t->next;
            delete t;
        }
    }
}
void Tarjan(int pre,int x,int ith)
{
    ++deep;
    dfn[x]=low[x]=deep;
    th[x]=ith;//第几次使用Tarjan
    visited[x]=true;
    in[x]=true;
    str.push(x);
    struct tt *t=mem[x].next;
    bool think=false;
    while(t!=NULL)
    {
        if(visited[t->j]==false)
        {
            Tarjan(x,t->j,ith);
            low[x]=min(low[x],low[t->j]);
        }else if(in[t->j]==true)
        {
            low[x]=min(low[x],dfn[t->j]);
        }else if(th[t->j]<ith)//如果出现前几次用Tarjan搜过的点 则x所在的联通分量也不可能是根
        {
            think=true;
        }
        t=t->next;
    }
    if(think)
    {
        f[x]=true;
    }
    if(low[x]==dfn[x])
    {
        int num=0;
        while(str.top()!=x)
        {
            uppoint[str.top()]=x;//缩点
            in[str.top()]=false;
            ++num;
            str.pop();
        }
        uppoint[str.top()]=x;
        in[str.top()]=false;
        ++num;
        str.pop();
        sum[x]=num;
        f[pre]=true;//前一个点为父结点 不可能是根
    }
}
int main()
{
    int n,m;
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        while(m--)
        {
            int i,j;
            scanf("%d %d",&i,&j);
            build(i,j);
        }
        memset(th,-1,sizeof(th));
        memset(dfn,-1,sizeof(dfn));
        memset(visited,false,sizeof(visited));
        memset(in,false,sizeof(in));
        memset(f,false,sizeof(in));
        memset(uppoint,-1,sizeof(uppoint));
        memset(sum,0,sizeof(sum));
        deep=0;
        for(int i=1,t=1;i<=n;++i)
        {
            if(dfn[i]==-1)//防止一次搜不完 t 是第几次搜
            Tarjan(0,i,t++);
        }
        for(int i=1;i<=n;++i)
        {
            if(f[i])//把不可能是根联通分量的点的标记转移的缩点上
            f[uppoint[i]]=true;
        }
        int num=0;
        int k=0;
        for(int i=0;i<=n;++i)
        {
            if(i==uppoint[i]&&!f[i])
            {
                ++num;
                k=i;
            }
        }
        if(num!=1)//不只一个根联通分量
        {
            printf("0\n");
        }
        else
        {
            printf("%d\n",sum[k]);
        }
        Clearlist(n);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/liulangye/p/2529861.html