hdu3861——tarjan縮點+樹形dp

題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=3639

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

題目翻譯:

幼兒園的孩子喜歡玩一種叫做"鷹和雞"的遊戲。但總有一個大問題:在這個遊戲裏,每個孩子都想扮演老鷹的角色。‎
‎於是老師想出了一個主意:投票。每個孩子都有一些漂亮的手帕,如果他/她認爲某人適合霍克的角色,他/她給這個孩子一個手帕,這意味着這個得到手帕的孩子贏得了支持。請注意,支持可以傳輸。獲得最多支持的孩子在投票中獲勝,並能夠扮演"鷹"的角色。(注意:如果 A 可以‎
‎贏得 B (A ! = B) 的支持,則 A 在任何情況下都只能從 B 贏得一個支持,從 B 傳輸到 A 的支持數量很多。A在任何情況下都不能贏得自己的支持。‎
‎如果兩個或兩個以上的孩子擁有相同數量的支持,我們會把他們都當作勝利者。‎
‎這裏有一個示例:3個孩子A,B和C,A給B手帕,B給C手帕,所以C贏得2個支持,他選擇成爲老鷹。‎

‎輸入‎

‎有幾個測試用例。首先是整數 T(T <= 50),表示測試用例的數量。‎
‎每個測試用例從兩個整數 n 開頭,m 在一行中(2 <= n <= 5000,0 <m <= 30000)。n 表示有 n 個子級(編號從 0 到 n - 1)。以下每行包含兩個整數 A 和 B(A != B),表示編號爲 A 的子項向 B 提供手帕。‎

‎輸出‎

‎對於每個測試用例,輸出應首先包含一行"case x:",此處 x 表示案例編號從 1 開始。後面跟一個數字,這是獲勝者得到‎‎的總支持。‎
‎然後沿着包含所有鷹隊的號碼的線走。數字必須按增加順序列出,並用單個空格分隔。

 

這個題的做法很巧妙,如果我們正面直接去做的話,代碼很不容易實現,因此,秉着正難則反的原則,我們要想想怎麼去反着來。其實就是先按順序去建圖,同時存下來每個連通分量的權值,然後縮點後再逆着建圖,很容易知道入度爲0就是勝利者。

之後再對逆着建的圖進行dfs,求出總支持個數=它所在的強連通分量中的點數-1+後代中的總支持個數。

#include <bits/stdc++.h>
using namespace std;
const int maxn=5e3+7;
int pre[maxn],low[maxn],sccno[maxn],num[maxn];
int in0[maxn];
int visited[maxn],f[maxn];
int n,m;
int dfs_clock,scc_cnt;
stack<int> s;
vector<int> G[maxn];
vector<int> G2[maxn];
void dfs(int u){
	low[u]=pre[u]=++dfs_clock;
	s.push(u);
	for(int i = 0;i<(int)G[u].size();++i){
		int v=G[u][i];
		if(!pre[v]){
			dfs(v);
			low[u]=min(low[u],low[v]);
		}
		else if(!sccno[v]){
			low[u]=min(low[u],pre[v]);
		}
	}
	if(low[u]==pre[u]){
		scc_cnt++;
		num[scc_cnt]=0;
		while(1){
			int x=s.top();
			s.pop();
			sccno[x]=scc_cnt;
			num[scc_cnt]++;
			if(x==u) break;
		}
	}
}
void find_scc(int n){
	dfs_clock=scc_cnt=0;
	memset(pre,0,sizeof(pre));
	memset(low,0,sizeof(low));
	memset(sccno,0,sizeof(sccno));
	for(int i = 0;i<n;++i)
		if(!pre[i])
			dfs(i);
}
int dfs2(int u){
	int sum=0;
	visited[u]=1;
	for(int i = 0;i<(int)G2[u].size();++i){
		int v=G2[u][i];
		if(!visited[v]){
			sum+=dfs2(v)+num[v];
		}
	}
	return sum;
}
int main(int argc, char** argv) {
	int T;
	cin>>T;
	for(int ncase=1;ncase<=T;++ncase){
		printf("Case %d: ",ncase);
		scanf("%d%d",&n,&m);
		for(int i = 0;i<n;++i) G[i].clear();
		while(m--){
			int x,y;
			scanf("%d%d",&x,&y);
			G[x].push_back(y);
		}
		find_scc(n);
		for(int i = 1;i<=scc_cnt;++i){
			G2[i].clear();
			in0[i]=1;
		}
		for(int u = 0;u<n;++u){
			for(int  i=0;i<(int)G[u].size();++i){
				int v=G[u][i];
				if(sccno[u]!=sccno[v]){
					in0[sccno[u]]=0;
					G2[sccno[v]].push_back(sccno[u]);
				}
			}
		}
		memset(f,0,sizeof(f));
		int maxf=-1;
		for(int i = 1;i<=scc_cnt;++i){
			if(in0[i]){
				memset(visited,0,sizeof(visited));
				f[i]=num[i]-1+dfs2(i);
				maxf=max(maxf,f[i]);
			}
		}
		int first=1;
		printf("%d\n",maxf);
		for(int i = 0;i<n;++i){
			if(f[sccno[i]]==maxf){
				if(first) printf("%d",i),first=0;
				else printf(" %d",i); 
			}
		}
		printf("\n");
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章