HDU 3639 Hawk-and-Chicken【强连通分量+缩点反向建图】

H a w k − a n d − C h i c k e n Hawk-and-Chicken HawkandChicken

Time Limit: 6000/2000 MS (Java/Others)Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4638Accepted Submission(s): 1414

Problem Description
Kids in kindergarten enjoy playing a game called Hawk-and-Chicken. But there always exists a big problem: every kid in this game want to play the role of Hawk.
So the teacher came up with an idea: Vote. Every child have some nice handkerchiefs, and if he/she think someone is suitable for the role of Hawk, he/she gives a handkerchief to this kid, which means this kid who is given the handkerchief win the support. Note the support can be transmitted. Kids who get the most supports win in the vote and able to play the role of Hawk.(A note:if A can win
support from B(A != B) A can win only one support from B in any case the number of the supports transmitted from B to A are many. And A can’t win the support from himself in any case.
If two or more kids own the same number of support from others, we treat all of them as winner.
Here’s a sample: 3 kids A, B and C, A gives a handkerchief to B, B gives a handkerchief to C, so C wins 2 supports and he is choosen to be the Hawk.

Input
There are several test cases. First is a integer T(T <= 50), means the number of test cases.
Each test case start with two integer n, m in a line (2 <= n <= 5000, 0 <m <= 30000). n means there are n children(numbered from 0 to n - 1). Each of the following m lines contains two integers A and B(A != B) denoting that the child numbered A give a handkerchief to B.

Output
For each test case, the output should first contain one line with “Case x:”, here x means the case number start from 1. Followed by one number which is the total supports the winner(s) get.
Then follow a line contain all the Hawks’ number. The numbers must be listed in increasing order and separated by single spaces.

Sample Input
2
4 3
3 2
2 0
2 1

3 3
1 0
2 1
0 2

Sample Output
Case 1: 2
0 1
Case 2: 2
0 1 2

Author
Dragon

Source
2010 ACM-ICPC Multi-University Training Contest(19)——Host by HDU

题目大意:

有n个人投票(不能投给自己),一共有m次投票,如果a投给b,a就支持b;如果a投给b,b投给c,a、b就都支持c(有传递性),问得到支持最多的有多少,那些人得到支持最多。

分析:

首先可以发现以下两个关系:
①在同一个强连通分量中的人可以相互投票,如果强连通分量中有m个人,那么强连通分量中的每个人都能得到m-1的票。
②在缩点之后的图中,得到最多票的人只可能出现在出度为0的强连通分量中,因为如果还有出度,那么连接的下一个点得到的票肯定比当前多。

根据上面两个关系,我们可以先进行强连通分量分解,把每个强连通分量缩成一个点,记录每个强连通分量中的人的个数。
由于正向建图会重复计算人数,所以得要对缩点之后的各点进行反向建图,建图之后对每个入度为0的点(也就是正向建图时出度为0的点)进行dfs,算出这个强连通分量中每个人的得票数,记录下来,最后检查一遍每个点是否符合得票数最大就ok。

AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 5e3+7;
const int maxm = 3e4+7;
struct EDGE{
	int to,next;
};
int n,m,T,kase=0,index,top,number,edge_num,edge_DAG_num,maxx;
EDGE edge[maxm],edge_DAG[maxm];
int head[maxn],head_DAG[maxn],stk[maxn],dfn[maxn],low[maxn],instk[maxn],scc_id[maxn],indegree[maxn],scc_ele_num[maxn],spt[maxn];
int used[maxn];
void init(){
	index = top = number = edge_num = edge_DAG_num = maxx = 0;
	memset(indegree,0,sizeof(indegree));
	memset(scc_ele_num,0,sizeof(scc_ele_num));
	memset(head,255,sizeof(head));
	memset(head_DAG,255,sizeof(head_DAG));
	memset(dfn,0,sizeof(dfn));
	memset(instk,0,sizeof(instk));
	memset(low,0,sizeof(low));
	memset(used,0,sizeof(used));
	memset(spt,0,sizeof(spt));
}
void add_edge(int u,int v){
	edge[edge_num].to = v;
	edge[edge_num].next = head[u];
	head[u] = edge_num++;
}
void tarjan(int u){
	dfn[u] = low[u] = ++index;
	stk[++top] = u; instk[u] = 1;
	for(int i=head[u];i!=-1;i=edge[i].next){
		EDGE e = edge[i];
		if(!dfn[e.to]){
			tarjan(e.to);
			low[u] = min(low[u],low[e.to]);
		}
		else if(instk[e.to]) low[u] = min(low[u],dfn[e.to]);
	}
	if(dfn[u]==low[u]){
		number++;
		while(stk[top]!=u){
			scc_id[stk[top]] = number;
			instk[stk[top--]] = 0;
			scc_ele_num[number]++;
		}
		scc_id[stk[top]] = number;
		instk[stk[top--]] = 0;
		scc_ele_num[number]++;
	}
}
void input(){
	init();
	scanf("%d %d",&n,&m);
	for(int i=0;i<m;i++){
		int pa,pb;
		scanf("%d %d",&pa,&pb);
		add_edge(pa,pb);
	}
}
void add_DAG_edge(int u,int v){
	edge_DAG[edge_DAG_num].to = v;
	edge_DAG[edge_DAG_num].next = head_DAG[u];
	head_DAG[u] = edge_DAG_num++;
	indegree[v]++;
}
void build_DAG(){
	for(int i=0;i<n;i++) for(int j=head[i];j!=-1;j=edge[j].next){
		EDGE &e = edge[j];
		if(scc_id[i]!=scc_id[e.to]) add_DAG_edge(scc_id[e.to],scc_id[i]);
	}
}
void dfs(int now,int &res,int k){
	used[now] = k;
	res+=scc_ele_num[now];
	for(int i=head_DAG[now];i!=-1;i=edge_DAG[i].next){
		EDGE e = edge_DAG[i];
		if(used[e.to]!=k) dfs(e.to,res,k);
	}
}
int main(){
	scanf("%d",&T);
	while(T--){
		input();
		for(int i=0;i<n;i++) if(!dfn[i]) tarjan(i);
		build_DAG();				//反向建图 
		int res;
		for(int i=1;i<=number;i++) if(indegree[i]==0){
			res = -1;
			dfs(i,res,i);
			spt[i] = res;
			maxx = max(maxx,res);
		} 
		printf("Case %d: %d\n",++kase,maxx);
		bool flag = true;
		for(int i=0;i<n;i++){
			if(spt[scc_id[i]]==maxx){
				if(flag) printf("%d",i),flag=false;
				else printf(" %d",i);
			}
		}
		printf("\n");
	}
	return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值