(这是从最近完成的编程竞赛中得出的)
给定一个具有N个节点和N-1条边的连通图G。(请注意,这意味着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;
}