Popular Cows poj 2186(强连通分量+缩点 )

Popular Cows
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 32103 Accepted: 13094

Description

Every cow's dream is to become the most popular cow in the herd. In a herd of N (1 <= N <= 10,000) cows, you are given up to M (1 <= M <= 50,000) ordered pairs of the form (A, B) that tell you that cow A thinks that cow B is popular. Since popularity is transitive, if A thinks B is popular and B thinks C is popular, then A will also think that C is 
popular, even if this is not explicitly specified by an ordered pair in the input. Your task is to compute the number of cows that are considered popular by every other cow. 

Input

* Line 1: Two space-separated integers, N and M 

* Lines 2..1+M: Two space-separated numbers A and B, meaning that A thinks B is popular. 

Output

* Line 1: A single integer that is the number of cows who are considered popular by every other cow. 

Sample Input

3 3
1 2
2 1
2 3

Sample Output

1

Hint

Cow 3 is the only cow of high popularity. 

在学习了tarjan算法求解强连通分量之后就接触到 强连通缩点 ,但是就是不知道怎么运用tarjan算法来找缩点,后来接触了几个有关缩点的题目,才了解到缩点的关键所在;

对于一个图,我们进行强连通分量求解之后,进行缩点操作, 缩点的最大好处在于把一个杂乱无章的有向图变成一个有向无环图, 而在有向无环图中,有两种点比较特殊:一种是入度为 0 的点,另一种是 出度为 0 的点。我们把入度为0的点就叫做根,出度为0的点叫做叶子,可以参见下图

 对于图中的绿色点就是叶子,红色的点就是根,未标记的就是中间点,但是注意:这里是缩点之后形成的有向无环图,图中的每一个点就是一个强连通分量, 那么我们怎么把一个图缩点成为一个有向无环图呢 ?那么这里就用到一个数组来“ 染色 ”,我们在求强连通分量的时候有一个 Bcnt 用来记录强连通分量个数,有一个数组Belong[MAX]来染色,Belong[i] =Bcnt 就表示 i 这个元素属于第Bcnt个强连通分量,在把所有的点求完强连通分量之后,我们只是得到一个所有点的归属数组Belong[MAX],那么,之后我们就用两个数组 in[MAX],out[MAX]表示缩点后的有向无环图的点的入度和出度,用以下代码求解缩点形成的图 

memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
for(i = 1; i <= n; i ++)
{
	for(int k = head[i]; k != -1; k = edge[k].next)
	{
		j = edge[k].to;
		if(Belong[i] != Belong[j])//注意这里就是所属的强连通分量,也就是属于哪一个缩点
			out[Belong[i]] ++, in[Belong[j]] ++;
	}
}
那么我们就得到这个有向无环图的关系,那么下面给出两个有关缩点的题目

poj2186Popular cow

题意:有N头牛,M个关系,每个关系为 a b,表示牛a认为牛b 收欢迎,那么问根据所给信息判断有多少头牛是收到所有的牛的欢迎,而且这里a认为b受欢迎,b认为c受欢迎,那么a也会认为c受欢迎,

分析:这里我们根据M个关系建图,然后对于这个图缩点,那么,根据上面的缩点分析,我们只要求叶子的个数,因为别的缩点都指向叶子,叶子处在最高层,就是最受欢迎的,那么我们根据所有缩点的out[i]=0的个数,判断,如果只有一个,那么这个缩点(强连通分量)的牛都是最受欢迎的,(这里注意,叶子数为0就是表示只有一个强连通分量,那么所有的牛都是最受欢迎的),如果有多个就输出0

#include <stdio.h>
#include <string.h>

#define Max 10001
//建图
struct Edge
{
    int to;
    int next;
}edge[Max*5];

int head[Max],tol;
void add(int a, int b)
{
    edge[tol].to = b;
    edge[tol].next = head[a];
    head[a] = tol++;
}
//tarjan
int Belong[Max];//表示i在第几个强连通分量
int dfn[Max];//时间戳,也就是i是第几个访问的
int low[Max];//该节点及其子节点到达最小的dfn
int Stack[Max];//储存访问节点
bool instack[Max];//判断该节点是不是在栈里面
int out[Max];//看连通分量的出度
int Bcnt,time,top;//Bcnt 记录联通块的个数
                  //time 记录访问的次序
                  //top栈顶

void tarjan(int u)
{
    int v;
    dfn[u] = low[u] = ++time;//初始low[u] = dfn[u];
    Stack[top++] = u;//
    instack[u] = true;
    for( int i = head[u]; i != -1; i = edge[i].next)
    {
         v = edge[i].to;
        if(!dfn[v])// 如果v未被访问 则v是u的孩子
        {
            tarjan(v);
            if(low[u] > low[v])
                low[u] = low[v];
        }
        else//v被访问过
        {
            if(instack[v] && low[u] > dfn[v])
            {
                low[u] = dfn[v];
            }
        }
    }

    if(low[u] == dfn[u])//u为割点
    {
        Bcnt++;
        do
        {
            v = Stack[--top];
            instack[v] = false;
            Belong[v] = Bcnt;//记录v所在的联通快
        }while(u!=v);
    }
}

int main()
{
    int n,m;
    int u,v;
    scanf("%d%d",&n,&m);

    //建图
    tol = 0;
    memset(head,-1,sizeof(head));
    while(m--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);
    }
    //tarjan
    memset(dfn, 0, sizeof(dfn));
    Bcnt = top = time = 0;
    for( u = 1; u <= n; u++)
    {
        if(!dfn[u])
            tarjan(u);
    }
    //tarjan之后求缩点的出度
    memset(out, 0, sizeof(out));

    for( u = 1; u <= n; u++)
    {
        for( int j = head[u]; j != -1; j = edge[j].next)
        {
            v = edge[j].to;
            if(Belong[u] != Belong[v])
                out[Belong[u]]++;
        }
    }
    int num = 0, ans = 0,flag;//num记录缩点后出度为0的个数,flag记录入度为0的缩

    for( int i = 1; i <= Bcnt; i++)
    {
        if(out[i] == 0)
        {
            num++;
            flag = i;
        }
    }
    if(num == 1)//只有一个联通分量才有被所有牛仰慕的。否则不存在
    {
        for( int i = 1; i <= n; i++)
        {
            if(Belong[i] == flag)//这里计算属于这个连通分量的所有牛
                ans++;
        }
        printf("%d\n",ans);
    }
    else
        printf("0\n");
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值