(这来自最近完成的编程竞赛)
给你 G,一个有 N 个节点和 N-1 条边的连通图。
(请注意,这意味着 G 形成一棵树。)
G 的每条边都是有向的。(不一定向上到任何根)
对于 G 的每个顶点 v,可以反转零个或多个边,使得从每个其他顶点 w 到 v 都有一条有向路径。让实现这一点的边反转的最小可能数量为 f(v)。
我们可以通过什么线性或对数线性算法来确定具有最小总 f(v) 的顶点子集(包括这些顶点的 f(v) 的值)?
例如考虑具有这些边的 4 个顶点图:
A<--B
C<--B
D<--B
f(A) = 2, f(B) = 3, f(C) = 2 和 f(D) = 2...
..因此,所需的输出是 {A,C,D} 和 2
(注意我们只需要计算具有最小 f(v) 的顶点的 f(v) - 不是全部)
代码:
对于后代,这里是解决方案的代码:
int main()
{
struct Edge
{
bool fwd;
int dest;
};
int n;
cin >> n;
vector<vector<Edge>> V(n+1);
rep(i, n-1)
{
int src, dest;
scanf("%d %d", &src, &dest);
V[src].push_back(Edge{true, dest});
V[dest].push_back(Edge{false, src});
}
vector<int> F(n+1, -1);
vector<bool> done(n+1, false);
vector<int> todo;
todo.push_back(1);
done[1] = true;
F[1] = 0;
while (!todo.empty())
{
int next = todo.back();
todo.pop_back();
for (Edge e : V[next])
{
if (done[e.dest])
continue;
if (!e.fwd)
F[1]++;
done[e.dest] = true;
todo.push_back(e.dest);
}
}
todo.push_back(1);
while (!todo.empty())
{
int next = todo.back();
todo.pop_back();
for (Edge e : V[next])
{
if (F[e.dest] != -1)
continue;
if (e.fwd)
F[e.dest] = F[next] + 1;
else
F[e.dest] = F[next] - 1;
todo.push_back(e.dest);
}
}
int minf = INT_MAX;
rep(i,1,n)
chmin(minf, F[i]);
cout << minf << endl;
rep(i,1,n)
if (F[i] == minf)
cout << i << " ";
cout << endl;
}