POJ - 2186 Popular Cows 强连通分量 tarjan算法+缩点

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. 


题意: 输入n,m, n为有n头牛,m为有m中关系,下面有m行,每行有两个数,a,b,表示 b 受 a 欢迎; 若a欢迎b,b欢迎c,那么a也欢迎c,求,有几只牛除自身外受全部的牛欢迎;

强连通分量:有向图中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,两个定点形成互达,则称两个顶点强连通(strongly connected),强连通分量,可能是个圆形结构,也有可能是网状结构,一定要记住还有网状结构;

思路: 先用 tarjan 算法,求出相互受欢迎的,相互受欢迎的就是一个强连通分量,再让强连通分量进行缩点;求出缩点后的树(图),出度为0(叶子)的节点个数;

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#include<stack>
#include<vector>

#define Max 10100
vector<int >v[Max];
int n,m;
int dfn[Max];   // 时间戳
int low[Max];   //  为i或i的子树能够追溯到的最早的栈中节点的次序号
int instack[Max]; // 判断是不是在栈中,为了避免重复
int color[Max]; // 染色 强连通分量,为了进行缩点;
int dfn_num;  //  为每一个节点加上时间戳
int color_num;  //  强连通分量的数目


stack<int > s;

void init()
{
	int i;
	for(i=0;i<=n;i++)
		v[i].clear();
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(instack,0,sizeof(instack));
	memset(color,0,sizeof(color));
}

void Tarjan(int x)    //  tarjan 算法 
{
	low[x] = dfn[x] = dfn_num++;
	instack[x] = 1;
	s.push(x);
	int i;
	for(i = 0;i<v[x].size();i++)
	{
		int k = v[x][i]; 
		if(!dfn[k])
		{
			Tarjan(k);
			low[x] = min(low[x],low[k]);
		}
		else if(instack[k])
		{
			low[x] = min(low[x],dfn[k]);
		}
	}
	if(dfn[x] == low[x])
	{
		instack[x]  = 0;
		color[x] = ++color_num;
		int t;
		do
		{
			t = s.top();
			s.pop();
			instack[t] = 0;
			color[t] = color_num;
			//if(t!=x)printf("%d ",t);
		}while(t!=x);
		//printf("%d\n",x);
	}
}

int hhh()
{
	int i,j;
	int num[Max];  // 每个强连通分量中有多少节点(原图,未经缩点之前的图)
	int out[Max]; //每一个经缩点之后的节点(每个强连通分量)的出度;
	memset(num,0,sizeof(num));
	memset(out,0,sizeof(out));

	for(i=1;i<=n;i++)
	{
		num[color[i]]++;
		for(j=0;j<v[i].size();j++)
		{
			int k=v[i][j];
			if(color[i]!=color[k])
				out[color[i]]++;
		}

	}
	int con=0,t;
	for(i=1;i<=color_num;i++)
	{
		if(out[i]==0)
		{
			con++;
			t=i;
		}
	}
	return (con==1)?num[t]:0;  // 这个信息量很大,一个图经缩点之后一定是树,但这个首先只能是一个树,
						//若是两个树或多个树,经缩点之后的图,出度为0的节点数一定会大于等于2;
						// 若是一颗树,那么经缩点之后的树,只能有一个叶子,
						//若多个叶子,这个多个叶子谁也不欢迎谁,那么没有人会是最受欢迎的人;
}
int main()
{
	int i,j;
	while(~scanf("%d%d",&n,&m))
	{
		init();
		int x,y;
		for(i=0;i<m;i++)
		{
			scanf("%d%d",&x,&y);
			v[x].push_back(y);
			//v[y].push_back(x);
		}
		dfn_num = 1;
		color_num = 0;
		for(i=1;i<=n;i++)
			if(!dfn[i])
				Tarjan(i);
		printf("%d\n",hhh());
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值