POJ2186 USACO 2003 Fall Popular Cows(强联通分支)

题目大意:有n只牛,给出一个受欢迎的关系,受欢迎的关系是单向的但是可以传递,当然,每只牛都是欢迎自己的。问有多少只牛收到所有牛的欢迎。


思路:首先我们要明确这是一张有向图。然后求出所有的强联通分量,把它们压缩成一个点,之后我们就会得到一棵树。最后统计树上入度为0的点,如果有不止一个入度为0的点,那么肯定是不符合题意的,因为这些入度为0的点不会互相欢迎。注意在这里树上入度为0的点可能并不是真正的一个点,有可能是经过我们压缩的强联通分支。


#include<cstdio>
#include<cstring>
#define MAXN 10005
#define Min(a,b) a<b?a:b
using namespace std;
struct e
{
	int v;
	int next;
}edge[100005];
int cnt;
int head[MAXN];
void add_edge(int u,int v)
{
	edge[cnt].v = v;
	edge[cnt].next = head[u];
	head[u] = cnt++;
}
int n,m;
int dcnt,low[MAXN],dfn[MAXN];
bool instack[MAXN];
int stack[MAXN],tot;
int cut_e;
int belong[MAXN];
int in[MAXN];
void dfs(int u)
{
	low[u] = dfn[u] = ++dcnt;//按访问先后顺序标号
	stack[++tot] = u;//u进栈
	instack[u] = true;//判断横叉边
	for(int i = head[u]; i != -1; i = edge[i].next)
	{
		int v = edge[i].v;
		if(dfn[v] == -1)
		{
			dfs(v);//u,v为父子边
			low[u] = Min(low[u],low[v]);
		}
		else if(instack[v])//u,v是返祖边
		{
			low[u] = Min(low[u],dfn[v]);
		}
	}
	if(low[u] == dfn[u])//u是连通分量的根
	{
		cut_e++;//连通分量数加1
		do
		{
			belong[stack[tot]] = cut_e;//记录属于第几个连通分量
			instack[stack[tot]] = false;//出栈
			tot--;
		}while(stack[tot+1] != u);
	}
}
int main()
{
	while(scanf("%d%d",&n,&m) != EOF)
	{
		memset(head,-1,sizeof head);
		memset(edge,0,sizeof edge);
		memset(low,0,sizeof low);
		memset(dfn,-1,sizeof dfn);
		memset(instack,0,sizeof instack);
		memset(stack,0,sizeof stack);
		memset(belong,0,sizeof belong);
		memset(in,0,sizeof in);
		tot = 0;
		cnt = dcnt = 0;
		cut_e = 0;
		for(int i = 1; i <= m; i++)
		{
			int a,b;
			scanf("%d%d",&a,&b);
			add_edge(a,b);//有向图,立碑纪念
		}
		for(int i = 1; i <= n; i++)
			if(dfn[i] == -1) 
				dfs(i);
		for(int i = 1; i <= n; i++)//同一个连通分量里的所有点看做一个点
		{
			for(int j = head[i]; j != -1; j = edge[j].next)
			{
				int v = edge[j].v;
				if(belong[i] != belong[v])//统计入度
				{
					in[belong[i]]++;
				}
			}
		}
		int cnt = 0,cntx;
		for(int i = 1; i <= cut_e; i++)//统计入度,只能有一个入度为0的点
			if(!in[i]) 
			{
				cnt++;
				cntx = i;
			}
		if(cnt == 1)
		{
			int sum = 0;
			for(int i = 1; i <= n; i++)
				if(belong[i] == cntx)
					sum++;
			printf("%d\n",sum);
		}
		else printf("0\n");
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值