题意:
有n个城市,它们之间有n-1条路(路都是单向的),形成一棵树。现在可以改变一些路的方向。
让你选择一个首都,使得从首都出发到达到其它城市,所需要重建的路的数量最少。
思路:
先进行一次dfs,求出每个点到达它的所有子节点所需要重建的路的数量(此时不需要理会父节点的情况,仅对子树)。
再进行一次dfs,更新每个点作为首都时所应改变的路的实际数量(对整棵树而言),这个可以自己思考一下应该怎么更新。
*建图的时候,需要保存为双向边,要记录一下哪条边才是实际存在的边。
AC代码:
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int MAXN = 2*1e5+5;
struct Edge
{
int v, next;
bool flag;
};
int dp[MAXN], n, pp;
int head[MAXN];
Edge edge[2*MAXN];
bool vis[MAXN];
int res[MAXN], minres;
void addEdge(int u, int v, bool flag)
{
edge[pp] = (Edge){v, head[u], flag};
head[u] = pp++;
}
void dfs(int u)
{
dp[u] = 0;
int next = head[u];
vis[u] = true;
while(next != -1)
{
Edge& e = edge[next];
if(!vis[e.v])
{
dfs(e.v);
dp[u] += dp[e.v];
if(e.flag == false) dp[u]++;
}
next = e.next;
}
}
void dfs2(int u)
{
int next =head[u];
vis[u] = true;
while(next != -1)
{
Edge &e = edge[next];
if(!vis[e.v])
{
if(e.flag == false) res[e.v] = res[u]-1; //res[e.v] = dp[e.v]+res[u]-dp[e.v]-1;
else res[e.v] = res[u]+1; //res[e.v] = dp[e.v]+res[u]-dp[e.v]+1;
minres = min(minres, res[e.v]);
dfs2(e.v);
}
next = e.next;
}
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d" ,&n);
int u, v;
minres = 2*n;
for(int i = 0;i < n-1; i++)
{
scanf("%d%d" ,&u, &v);
addEdge(u, v, true);
addEdge(v, u, false);
}
//
dfs(1);
memset(vis, false ,sizeof(vis));
//
/*
cout<<"test"<<endl;
for(int i = 1;i <= n; i++)
cout<<dp[i]<<" ";
puts("");*/
res[1] = dp[1];
minres = min(minres, res[1]);
dfs2(1);
printf("%d\n", minres);
int mark = 0;
for(int i = 1;i <= n; i++)
{
if(res[i] == minres)
{
if(mark == 0) printf("%d", i), mark = 1;
else
printf(" %d",i);
}
}
puts("");
//
return 0;
}