Popular Cows-POJ2186Tarjan

Time Limit: 2000MS   Memory Limit: 65536K
     

Descriptionnode

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.

Inputide

* 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.

Outputthis

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

Sample Inputspa

3 3
1 2
2 1
2 3

Sample Outputcode

1

Hintorm

Cow 3 is the only cow of high popularity.

Sourceblog

USACO 2003 Fall

题意:每个奶牛都想成为牧群中最受仰慕的奶牛,在牧群中有n头奶牛,给定m对关系(A,B),表示A奶牛仰慕B奶牛。计算牧群中被其余奶牛仰慕的奶牛的数目。

思路:将n头奶牛的m个关系将会构建一个有向图,在图中强连通份量中的任意奶牛必定是被份量中的其余奶牛仰慕。因此问题就转化为在图中将强连通份量进行缩点,在造成的新图中,若是一个强连通份量集合的出度为零,说明这个集合被其余集合仰慕,而不仰慕其余的集合,因此若是在新图中集合出度为零的数目不大于1,则为出度为零集合中奶牛的数目,若是大于1,则出度为零集合之间没有仰慕关系的,因此结果为0.

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <algorithm>

using namespace std;

const int Max = 10100;

typedef struct node
{
	int v;

	int next;

}Line;

vector<int>G[Max];

Line Li[Max*5];

int Head[Max],top;

int dfn[Max],low[Max],pre[Max];

int num[Max],Num,Du[Max];

int dep;

bool vis[Max];

int ans;

stack<int>S;

void AddEdge(int u,int v)
{
	Li[top].v=v ; Li[top].next=Head[u];

	Head[u]=top++;
}

void Tarjan(int u)// Tarjan求强连通份量
{
	dfn[u]=low[u]=dep++;

	S.push(u);
	
	for(int i=Head[u];i!=-1;i=Li[i].next)
	{
		if(dfn[Li[i].v]==-1)
		{
			Tarjan(Li[i].v);

			low[u]=min(low[u],low[Li[i].v]);
		}
		else
		{
			low[u]=min(low[u],dfn[Li[i].v]);
		}
	}

	if(low[u]==dfn[u])//强连通份量的根节点
	{

		while(!S.empty())
		{
			int v = S.top();

			S.pop();

			pre[v] = Num;//给份量集合标号

			G[Num].push_back(v);//记录集合对应的点

			num[Num]++;

			if(v==u)
			{
				break;
			}
		}

		Num++;
	}

}

int main()
{

	int n,m;

	while(~scanf("%d %d",&n,&m))
	{
		top = 0;

		memset(Head,-1,sizeof(Head));

		int u,v;

		for(int i=0;i<m;i++)
		{
			scanf("%d %d",&u,&v);

			AddEdge(u,v);
		}

		memset(dfn,-1,sizeof(dfn));
	
		memset(num,0,sizeof(num));

		for(int i =0;i<=n;i++)
		{
			G[i].clear();
		}

		dep = 0;Num = 0;

		for(int i=1;i<=n;i++)
		{
			if(dfn[i]==-1)
			{
				Tarjan(i);
			}
		}

		memset(Du,0,sizeof(Du));

		for(int i=0; i<Num ;i++) //判断强连通份量的出度
		{
			memset(vis,false,sizeof(vis));

			for(int k=0;k<G[i].size();k++)
			{
				for(int j=Head[G[i][k]]; j!=-1;j=Li[j].next)
				{
					if(i!=pre[Li[j].v])
					{
						if(!vis[pre[Li[i].v]])//因为不一样的强连通份量之间相连的边可能不止一条,因此要判断是否是已经统计过。
						{
							vis[pre[Li[i].v]]=true;

							Du[i]++;
						}
					}
				}
			}
		}

		ans = 0;

		int ant  = 0;

		for(int i=0;i<Num;i++)//若是超过一个的出度为零的强连通份量,则这些连通份量就不能被其余的牛都仰慕。
		{
			if(Du[i]==0)
			{
				ans+=num[i];

				ant++;

			}
		}


		printf("%d\n",ant<=1?ans:0);

	}

	return 0;
}
相关文章
相关标签/搜索