poj 2186 Popular Cows

32 篇文章 0 订阅
类型:有向图连通性

题目:有n头牛,m个形式如(A, B)表示牛B受牛A欢迎,求受所有牛欢迎的牛的数量

来源: USACO 2003 Fall

思路:在同一强连通分量中,不同牛之间相互受欢迎,可以将其看做一个点[缩点],构造新图。假设新图连通,如果某个牛受所有其他牛欢迎,则其顶点的出度为0,其他点出度不为0,否则将不存在牛受其他所有牛欢迎。如果新图不连通,则存在多个出度为0的点。所以可以通过判断新图点出度为0的个数是否为1来判断解是否存在,如果存在,输出该强连通分量中的点

// poj 2186 Popular Cows
// ac 676K 79MS
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

#define MIN(a,b) (a) < (b) ? (a) : (b)
#define clr(a,b) memset(a,b,sizeof(a))
#define FOR(i,a,b) for((i) = (a); (i) < (b); ++i)
#define FORE(i,a,b) for((i) = (a); (i) <= (b); ++i)
#define MAXN 10100
#define MAXM 50100

bool vis[MAXN];
int num, cnt, count_node, kk, m, n, cnt_num;
int top, u[MAXM], v[MAXM], step[MAXN], low[MAXN];
int head[MAXN],belong[MAXN],stack[MAXN];
struct edge{
    int v,nxt;
}e[MAXM];

void Tarjan(int u) {
	int i, j, v;

	step[u] = low[u] = ++num;
	stack[++top] = u, vis[u] = true;//in_stack[u] = true;
	for(i = head[u]; i != -1; i = e[i].nxt) {
		v = e[i].v;
		if(!step[v]) {
			Tarjan(v);
			low[u] = MIN(low[u],low[v]);
		}
		else if(vis[v])
			low[u] = MIN(low[u],step[v]);
	}
	if(step[u] == low[u]){
		cnt_num++;
		do{
			j = stack[top--];
			vis[j] = false;
//			in_stack[j] = false;
			belong[j] = cnt_num;
		}while(j != u);
	}
}

int main() {
	int i,j,u, v;

	while(scanf("%d %d", &n, &m) != EOF){
		clr(head, -1);
		clr(step, 0);
		clr(belong, 0);
		clr(vis, false); //clr(in_stack,false);
		top = num = cnt = cnt_num = kk = count_node = 0;
		FOR(i, 0, m) {
			scanf("%d %d", &u, &v);
			e[cnt].v = v;
			e[cnt].nxt = head[u];
			head[u] = cnt++;
		}
		FORE(i, 1, n)
			if(!step[i])
                Tarjan(i);
		FORE(i, 1, n)
			for(j = head[i]; j != -1; j = e[j].nxt)
				if(belong[i] != belong[e[j].v])
					vis[belong[i]] = true;
		FORE(i, 1, cnt_num)
			if(!vis[i]) {
                count_node++;
                kk = i;
            }
		//!!!
		if(count_node != 1)
			printf("0\n");
		else{
			int ans = 0;
			FORE(j, 1, n)
				if(belong[j] == kk) ans++;
			printf("%d\n",ans);
		}
	}
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值