题意:一个国家有n个点,由单向边连接,现在要改变一些点到其所能到达的所有点的方向,问怎么改最小,并且输出那些点
思路:显然的对于某个结点,考虑的就是以它为根的子树和它的父节点要改变的数目,我们可以首先以点1作为这棵树的根结点,那么对于它来说dp[1]是已经决定的了,那么再又dp[1]递推到下面的子节点,这个过程就完成了
#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <map>
#include <string>
#include <set>
#include <ctime>
#include <cmath>
#include <cctype>
using namespace std;
#define maxn 200020
#define LL long long
#define inf 1e9
int cas=1,T;
struct Edge
{
int v,w;
Edge(int vv,int ww):v(vv),w(ww){}
};
vector<Edge>e[maxn];
int vis[maxn],dp[maxn];
int res;
void dfs(int root)
{
vis[root]=1;
for (int i = 0;i<e[root].size();i++)
{
int v = e[root][i].v,w=e[root][i].w;
if (!vis[v])
{
dfs(v);
res+=w;
}
}
}
void dfs1(int root)
{
vis[root]=1;
for (int i = 0;i<e[root].size();i++)
{
int v = e[root][i].v,w=e[root][i].w;
if (!vis[v])
{
if (w==0)
{
dp[v]=dp[root]+1;
}
else
dp[v]=dp[root]-1;
dfs1(v);
}
}
}
int main()
{
int n;
scanf("%d",&n);
memset(dp,0,sizeof(dp));
memset(vis,0,sizeof(vis));
/* for (int i = 0;i<=n;i++)
e.clear();*/
for (int i = 1;i<n;i++)
{
// e[i].clear();
int u,v;
scanf("%d%d",&u,&v);
e[u].push_back(Edge(v,0));
e[v].push_back(Edge(u,1));
}
dfs(1);
dp[1]=res;
memset(vis,0,sizeof(vis));
dfs1(1);
int minx=inf;
int pos;
for (int i = 1;i<=n;i++)
{
if (minx>=dp[i])
{
pos = i;
minx = dp[i];
}
}
printf("%d\n",minx);
for (int i = 1;i<=n;i++)
{
if (i==pos)
{
printf("%d\n",i);
break;
}
if (minx==dp[i])
printf("%d ",i);
}
//freopen("in","r",stdin);
//scanf("%d",&T);
//printf("time=%.3lf",(double)clock()/CLOCKS_PER_SEC);
return 0;
}