POJ - 2186 Popular Cows

本文介绍了一道关于牛群中流行度计算的问题,通过构建有向图并运用Tarjan算法找到强连通分量,进而解决寻找被所有其他牛认为流行的奶牛数量。文章详细解析了Tarjan算法的实现过程,包括初始化、递归查找以及缩点后的有向无环图处理。

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.

输入输出
3 3
1 2
2 1
2 3



1

Hint:Cow 3 is the only cow of high popularity.

题意

n牛m关系
a b表示a认为b很nb
如果a b又b c则有a c
问被其他所有牛认为都很nb的奶牛数量

分析

是一个有向图,tarjan找出强连通分量,然后把他看成一个点,做完以后你会得到一个有向无环图(有环就被缩点),所以找出度为0的点,如果这样点大于1个那他们谁也瞧不起谁,所以是0。当且仅当出度为0的点只有一个的时候,其他的点的牛都是他的舔狗。
tarjan算法,这篇“全网最详细”,我也不好再多解释,大家可以参照这位dl的解析。
至于缩点就是把同一联通块的点看作一个。
那么我这里是开一个id数组出栈的时候标记一下
再检查出度

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<functional>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn=1e4+5;
const int INF=0x3f3f3f3f;
vector <int> edge[maxn];
int n,m,tot,dfn[maxn],low[maxn];//tarjan组件
int id[maxn],id_cnt,stack[maxn],top,sum[maxn];//id为属于哪个强联通块 后者为记录的栈 及记录联通块中点的个数
//用id[]记录是否在栈
void init(){
	tot=id_cnt=top=0;
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(id,0,sizeof(id));
	memset(stack,0,sizeof(stack));
	memset(sum,0,sizeof(sum));
	for(int i=0;i<=n;i++) edge[i].clear();
}
void tarjan(int x) {
	dfn[x]=low[x]=++tot;
	stack[++top]=x;
	for(int i=0; i<edge[x].size();i++) {
		if(!dfn[ edge[x][i] ]) {
			tarjan(edge[x][i]);
			low[x]=min(low[x],low[edge[x][i]]);
		} else if(!id[edge[x][i]]) { //出栈必有id 不入栈时为上一判断情况
			low[x]=min(low[x],dfn[edge[x][i]]);
		}
	}
	if(low[x]==dfn[x]) {
		id_cnt++;
		while(top) {
			int num=stack[top--];
			id[num]=id_cnt;
			sum[id_cnt]++;
			//printf("%d %d %d %d\n",num,x,id_cnt,sum[id_cnt]);
			if(num==x) break;
		}
	}
	return;
}
int main() {
	int from,to;
	scanf("%d%d",&n,&m);
	init();
	for(int i=0;i<m;i++) {
		scanf("%d%d",&from,&to);
		edge[from].push_back(to);
	}
	for(int i=1;i<=n;i++) {
		if(!dfn[i]) tarjan(i);
	}
	int out[maxn]={0};
	for(int i=1;i<=n;i++) {
		for(int j=0;j<edge[i].size();j++){
			if(id[i]!=id[ edge[i][j] ]){//不属于同一联通块 则为联通块的连线
				out[id[i]]++; 
				//printf("%d %d %d %d %d\n",i,id[i],out[id[i]],edge[i][j],id[edge[i][j]]);
			}
		}
	}
	int total=0,pos=0;
	for(int i=1;i<=id_cnt;i++) {
		if(out[i]==0){
			total++;
			pos=i;
		}
	}
	if(total!=1) puts("0");
	else printf("%d\n",sum[pos]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值