1. 程式人生 > >【CodeForces 219D】Choosing Capital for Treeland(樹形dp)

【CodeForces 219D】Choosing Capital for Treeland(樹形dp)

The country Treeland consists of n cities, some pairs of them are connected with unidirectional roads. Overall there are n - 1 roads in the country. We know that if we don't take the direction of the roads into consideration, we can get from any city to any other one.

The council of the elders has recently decided to choose the capital of Treeland. Of course it should be a city of this country. The council is supposed to meet in the capital and regularly move from the capital to other cities (at this stage nobody is thinking about getting back to the capital from these cities). For that reason if city a

 is chosen a capital, then all roads must be oriented so that if we move along them, we can get from city a to any other city. For that some roads may have to be inversed.

Help the elders to choose the capital so that they have to inverse the minimum number of roads in the country.

Input

The first input line contains integer n (2 ≤ n ≤ 2·105) — the number of cities in Treeland. Next n - 1 lines contain the descriptions of the roads, one road per line. A road is described by a pair of integers si, ti (1 ≤ si, ti ≤ nsi ≠ ti) — the numbers of cities, connected by that road. The i

-th road is oriented from city si to city ti. You can consider cities in Treeland indexed from 1 to n.

Output

In the first line print the minimum number of roads to be inversed if the capital is chosen optimally. In the second line print all possible ways to choose the capital — a sequence of indexes of cities in the increasing order.

Examples

Input

3
2 1
2 3

Output

0
2 

Input

4
1 4
2 4
3 4

Output

2
1 2 3 

思路:

 

思路:

任選一個點作為根節點,dfs搜一遍,找到根節點需要轉方向的邊有多少,然後再來dfs從上往下推,每到一層就推出這一層的結果。例如根節點是1號它的子節點之一是2號,那麼1號和2號在2這可子樹上需要逆向的邊數是相同的,因為這一邊的資訊是2傳給1的。再看1號結點連的其他子樹(除2號這一支之外),當以2號為根節點時,2號會掃到1號,然後會掃1號的子節點,這樣1號和2號掃1號的子節點是相同的操作,因此 兩者得到的結果是相同的。這樣來說兩者數量的差別就體現在兩者之間的邊上。如果1掃到2時正向的,那麼2到1就是逆向的,2的逆向邊比1多,所以dp[2]=dp[1]+1;反之會比1少一條。因此我們可以藉助父節點與子節點之間的關係,從選定的根節點一直推到葉子節點。 需要用到兩次dfs

ac程式碼:

#include<stdio.h>
#include<string.h>
#include<queue>
#include<set>
#include<iostream>
#include<map>
#include<stack>
#include<cmath>
#include<algorithm>
#define ll long long
#define mod 1000000007
#define eps 1e-8
using namespace std;
const int MAXN=2e6+100;
struct node{
	int x,nex,w;
}side[MAXN*2];//加雙向邊一定記得開兩倍的陣列 
int n,u,v;
int head[MAXN],cnt=0;
int num[MAXN],dp[MAXN];
void init()
{
	memset(head,-1,sizeof(head));
	cnt=0;
	memset(dp,0,sizeof(dp));
	memset(num,0,sizeof(num));
}
void add(int x,int y,int w)
{
	side[cnt].x=y;
	side[cnt].nex=head[x];
	side[cnt].w=w;
	head[x]=cnt++;
	
}

void dfs(int x,int f)
{
	
	for(int i=head[x];i!=-1;i=side[i].nex)
	{
		int ty=side[i].x;
		if(ty==f)
		continue;
		dfs(ty,x);
		num[x]+=num[ty];
		if(!side[i].w)
		num[x]++;
	}
	dp[x]=num[x];
}
void df(int x,int f)
{
	for(int i=head[x];i!=-1;i=side[i].nex)
	{
		int ty=side[i].x;
		if(f==ty)//要判斷這一步否則會陷入死迴圈 
		continue; 
		if(!side[i].w)
		dp[ty]=dp[x]-1;
		else
		{
			dp[ty]=dp[x]+1;
		}
		df(ty,x);
	}
}
int main()
{
	init();
	scanf("%d",&n);
	for(int i=0;i<n-1;i++)
	{
		scanf("%d%d",&u,&v);
		add(u,v,1);//1表示邊原來是正向的(標準方向) 
		add(v,u,0);// 0表示不是標準方向 
	}
		dfs(1,-1);
		df(1,-1);
	int ans=dp[1];
	int tot=1,ot[MAXN];
	ot[0]=1;
	for(int i=2;i<=n;i++)
	{
		if(ans>dp[i])
		{
			ans=dp[i];
			ot[0]=i;
			tot=1;
		}
		else if(ans==dp[i])
		{
			ot[tot++]=i;
		}
	}
	printf("%d\n",ans);
	for(int i=0;i<tot;i++)
	{
		printf("%d",ot[i]);
		if(i==tot-1)
		printf("\n");
		else
		printf(" ");
	}
	
	return 0;
}

 

看了別人的思路感覺這樣想也挺清晰的,感覺這種轉換的思想挺好的,以後在題目中可能會用到這種思想,借鑑一下

思路:

把邊的方向化為權值,正向為1,逆向為0。

問題轉化為找哪些點的在遍歷全圖後總權值最大。

這就是樹形DP了,考慮每個節點,它可以從子樹收穫價值,也可以從父親收穫。所以dfs兩遍,一邊把子樹的價值存到dps[i]裡,再一遍把父親的價值存到dpf[i]裡。ans[i] = dps[i] + dpf[i]。

原文連結:https://blog.csdn.net/angon823/article/details/52316220