【发布时间】:2010-10-29 22:28:48
【问题描述】:
什么是获得树的最小顶点覆盖的好算法?
输入:
节点的邻居。
输出:
最小顶点数。
【问题讨论】:
-
听起来像是在为考试复习而“找不到”讲义。
-
+1 只是为了打败伟大而强大的 Welbog。
标签: algorithm tree dynamic-programming
什么是获得树的最小顶点覆盖的好算法?
节点的邻居。
最小顶点数。
【问题讨论】:
标签: algorithm tree dynamic-programming
我们需要为每个必须选择的节点找到最小的顶点覆盖,要么包含它,要么不包含它。但是根据每条边 (u, v) 的问题,'u' 或 'v' 中的任何一个都应该在封面中,所以我们需要注意如果当前顶点不包括在内,那么我们应该包括它的孩子,并且如果我们包含当前顶点,那么我们可能会也可能不会根据最佳解决方案包含其子节点。
这里,DP1[v] 对于任何顶点 v = 当我们包含它时。 DP2[v] 对于任何顶点 v = 当我们不包含它时。
DP1[v] = 1 + sum(min(DP2[c], DP1[c])) - 这意味着包括当前,可能包括也可能不包括其子级,具体取决于最佳情况。
DP2[v] = sum(DP1[c]) - 这意味着不包括当前,那么我们需要包括当前顶点的孩子。这里,c 是顶点 v 的子节点。
那么,我们的解是 min(DP1[root], DP2[root])
#include <bits/stdc++.h>
using namespace std;
vector<vector<int> > g;
int dp1[100010], dp2[100010];
void dfs(int curr, int p){
for(auto it : g[curr]){
if(it == p){
continue;
}
dfs(it, curr);
dp1[curr] += min(dp1[it], dp2[it]);
dp2[curr] += dp1[it];
}
dp1[curr] += 1;
}
int main(){
int n;
cin >> n;
g.resize(n+1);
for(int i=0 ; i<n-1 ; i++){
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 0);
cout << min(dp1[1], dp2[1]);
return 0;
}
【讨论】:
看了这里的答案,我还没有完全理解,所以我想我会发一个来自here的帖子
一般的想法是,您将树的根放在任意节点上,并询问该根是否在封面中。如果是,则通过递归计算以其子树为根的子树的最小顶点覆盖。如果不是,那么根的每个孩子都必须在顶点覆盖中,这样根和它的孩子之间的每条边都被覆盖。在这种情况下,您递归根的孙子。
例如,如果您有以下树:
A
/ \
B C
/ \ / \
D E F G
请注意,通过检查,您知道最小顶点覆盖是{B, C}。我们会找到这个最小封面。
这里我们从A 开始。
我们向下移动到B 和C 的两个子树,并对这个算法进行递归。我们不能简单地说B和C不在封面,因为即使AB和AC被覆盖,我们也不能说我们是否需要B和@987654332 @ 是否在封面。
(想想下面的树,它的根和它的一个孩子都在最小覆盖中({A, D})
A
/|\___
B C D
/|\
E F G
)
但我们知道AB和AC必须被覆盖,所以我们必须在封面上添加B和C。由于B 和C 在封面中,我们可以递归他们的孩子而不是递归B 和C(即使我们这样做了,它也不会给我们更多信息)。
设C(x) 为以x 为根的最小覆盖的大小。
那么,
C(x) = min (
1 + sum ( C(i) for i in x's children ), // root in cover
len(x's children) + sum( C(i) for i in x's grandchildren) // root not in cover
)
【讨论】:
我们可以使用基于 DFS 的算法来解决这个问题:
DFS(node x)
{
discovered[x] = true;
/* Scan the Adjacency list for the node x*/
while((y = getNextAdj() != NULL)
{
if(discovered[y] == false)
{
DFS(y);
/* y is the child of node x*/
/* If child is not selected as a vertex for minimum selected cover
then select the parent */
if(y->isSelected == false)
{
x->isSelected = true;
}
}
}
}
永远不会选择叶子节点作为顶点覆盖。
【讨论】:
我会简单地使用线性程序来解决最小顶点覆盖问题。 整数线性程序的公式可能类似于此处给出的公式:ILP formulation
我认为您自己的实现不会比这些高度优化的 LP 求解器更快。
【讨论】:
T(V,E) 是一棵树,这意味着对于任何叶子,任何最小顶点覆盖都必须包括叶子或与叶子相邻的顶点。这为我们提供了以下算法来找到 S,即顶点覆盖:
现在,剩下的就是验证如果原始树只有一个顶点,我们返回 1 并且从不开始递归,并且可以计算最小顶点覆盖。
编辑:
其实想了想,用一个简单的DFS变种就可以完成。
【讨论】:
{- Haskell implementation of Artem's algorithm -}
data Tree = Branch [Tree]
deriving Show
{- first int is the min cover; second int is the min cover that includes the root -}
minVC :: Tree -> (Int, Int)
minVC (Branch subtrees) = let
costs = map minVC subtrees
minWithRoot = 1 + sum (map fst costs) in
(min minWithRoot (sum (map snd costs)), minWithRoot)
【讨论】:
希望here你能找到更多相关问题的答案。
我正在考虑我的解决方案,可能您需要完善它,但只要动态编程在您的标签之一中,您可能需要:
看完this。更改了上述算法以找到最大独立集,因为在 wiki 文章中声明了
一个集合是独立的当且仅当它的补集是一个顶点覆盖。
因此,通过将 min 更改为 max,我们可以找到最大独立集并通过补充最小顶点覆盖,因为这两个问题是等价的。
【讨论】: