http://codeforces.com/problemset/problem/219/D
题意:
可以这么理解为—— 一些城市的道路之间是有向边,如果沿着路的方向来走花费的价值为 0,如果逆向行走花费的价值为 1,选择一些点为首都,使其到其他点所花费的价值最小
思路:
树形dp
运用两次深搜,第一次深搜求出点i到以其自身为根节点的子树上其他点总共需要花费的价值,dp[ i ] 记录。
第二次深搜,根据第一次dp的结果,利用父节点的信息来更新子节点,求出点i到整个图中其他任意点所花费的价值,dp[ i ] 记录。
如果root指向son dp[son] = dp[root] + 1;
如果son指向root dp[root] = dp[son] + 1;
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int N =2e5+5;
struct node
{
int v,w;
};
vector<node> g[N];
bool flag[N];
int dp[N];
queue<int> q;
void dfs(int root)
{
flag[root] = true;
int i;
for(i= 0; i < (int)g[root].size(); i++)
{
int son = g[root][i].v;
if(flag[son]) continue;
dfs(son);
if(g[root][i].w == 0)
{
dp[root] += dp[son] + 1;
}
else
{
dp[root] += dp[son];
}
}
}
void DFS(int root)
{
flag[root] = true ;
int i;
for(i = 0; i < (int) g[root].size(); i++)
{
int son = g[root][i].v;
if(flag[son]) continue;
if(g[root][i].w == 0)
{
dp[son] = dp[root] - 1; // 反向
}
else
{
dp[son] = dp[root] + 1; // 正向
}
DFS(son);
}
}
int main()
{
int n,i,u,v;
while(~scanf("%d",&n))
{
for(i = 1; i <= n; i++)
{
flag[i] = false;
g[i].clear();
dp[i] = 0;
}
node temp;
for(i = 1; i < n; i++)
{
scanf("%d%d",&u,&v);
temp.v = v;
temp.w = 1;
g[u].push_back(temp);
temp.v = u;
temp.w = 0;
g[v].push_back(temp);
}
dfs(1);
memset(flag,false,sizeof(flag));
DFS(1);
while(!q.empty())
{
q.pop();
}
int Min = N;
for(i = 1; i <= n; i++)
{
if(dp[i] < Min)
{
Min = dp[i];
while(!q.empty())
{
q.pop();
}
q.push(i);
continue;
}
if(dp[i] == Min)
{
q.push(i);
}
}
printf("%d\n",Min);
printf("%d",q.front());
q.pop();
while(!q.empty())
{
printf(" %d",q.front());
q.pop();
}
puts("");
}
return 0;
}