【问题标题】:What is a good algorithm for getting the minimum vertex cover of a tree?什么是获得树的最小顶点覆盖的好算法?
【发布时间】:2010-10-29 22:28:48
【问题描述】:

什么是获得树的最小顶点覆盖的好算法?

输入:

节点的邻居。

输出:

最小顶点数。

【问题讨论】:

  • 听起来像是在为考试复习而“找不到”讲义。
  • +1 只是为了打败伟大而强大的 Welbog。

标签: algorithm tree dynamic-programming


【解决方案1】:

我们需要为每个必须选择的节点找到最小的顶点覆盖,要么包含它,要么不包含它。但是根据每条边 (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;
} 

【讨论】:

    【解决方案2】:

    看了这里的答案,我还没有完全理解,所以我想我会发一个来自here的帖子

    一般的想法是,您将树的根放在任意节点上,并询问该根是否在封面中。如果是,则通过递归计算以其子树为根的子树的最小顶点覆盖。如果不是,那么根的每个孩子都必须在顶点覆盖中,这样根和它的孩子之间的每条边都被覆盖。在这种情况下,您递归根的孙子。

    例如,如果您有以下树:

        A
       / \
      B   C
     / \ / \
    D  E F  G
    

    请注意,通过检查,您知道最小顶点覆盖是{B, C}。我们会找到这个最小封面。

    这里我们从A 开始。

    A 在封面

    我们向下移动到BC 的两个子树,并对这个算法进行递归。我们不能简单地说BC不在封面,因为即使ABAC被覆盖,我们也不能说我们是否需要B和@987654332 @ 是否在封面。

    (想想下面的树,它的根和它的一个孩子都在最小覆盖中({A, D}

      A
     /|\___
    B C    D
          /|\
         E F G
    

    )

    A不在封面

    但我们知道ABAC必须被覆盖,所以我们必须在封面上添加BC。由于BC 在封面中,我们可以递归他们的孩子而不是递归BC(即使我们这样做了,它也不会给我们更多信息)。

    “正式”

    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
                )
    

    【讨论】:

      【解决方案3】:

      我们可以使用基于 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;
                 }
              }
         }
      }
      

      永远不会选择叶子节点作为顶点覆盖。

      【讨论】:

        【解决方案4】:

        我会简单地使用线性程序来解决最小顶点覆盖问题。 整数线性程序的公式可能类似于此处给出的公式:ILP formulation

        我认为您自己的实现不会比这些高度优化的 LP 求解器更快。

        【讨论】:

          【解决方案5】:

          T(V,E) 是一棵树,这意味着对于任何叶子,任何最小顶点覆盖都必须包括叶子或与叶子相邻的顶点。这为我们提供了以下算法来找到 S,即顶点覆盖:

          1. 在树中找到树的所有叶子(BFS 或 DFS),O(|V|)。
          2. 如果 (u,v) 是一条边,使得 v 是一片叶子,则将 u 添加到顶点覆盖,并修剪 (u,v)。这将为您留下一片森林 T_1(V_1,E_1),...,T_n(U_n,V_n)。
          3. 现在,如果 V_i={v},意味着 |V_i|=1,则可以删除该树,因为所有与 v 相关的边都已被覆盖。这意味着我们有一个递归的终止条件,我们有一个或没有顶点,我们可以计算 S_i 作为每个 T_i 的覆盖,并定义 S因为步骤 2 中的所有顶点联合每个 T_i 的覆盖。

          现在,剩下的就是验证如果原始树只有一个顶点,我们返回 1 并且从不开始递归,并且可以计算最小顶点覆盖。

          编辑:

          其实想了想,用一个简单的DFS变种就可以完成。

          【讨论】:

          • 没人真正关心这个答案吗?我认为这比标记的答案要好得多,因为它更简单
          【解决方案6】:
          {- 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)
          

          【讨论】:

            【解决方案7】:

            希望here你能找到更多相关问题的答案。


            我正在考虑我的解决方案,可能您需要完善它,但只要动态编程在您的标签之一中,您可能需要:

            1. 对于每个 u 顶点定义 S+(u) 是 顶点 u 和 S-(u) 的覆盖大​​小 没有顶点 u 的覆盖。
            2. 对于 u 的每个子 v,S+(u)= 1 + Sum(S-(v))。
            3. 对于 u 的每个子 v,S-(u)=Sum(max{S-(v),S+(v)})。
            4. 答案是 max(S+(r), S-(r)),其中 r 是树的根。

            看完this。更改了上述算法以找到最大独立集,因为在 wiki 文章中声明了

            一个集合是独立的当且仅当它的补集是一个顶点覆盖。

            因此,通过将 min 更改为 max,我们可以找到最大独立集并通过补充最小顶点覆盖,因为这两个问题是等价的。

            【讨论】:

            • 它没有多大帮助,我正在寻找一个伪代码或更详细的算法解释
            • 您需要找到一种方法来使用每个顶点的信息以某种方式计算覆盖的大小,因此为每个顶点变量定义将计算您是否包含顶点的大小,一般描述的算法将返回你的大小值,但你可以很容易地扩展它来构建一个表格,你将在每个步骤中存储你的选择。
            • 不一定需要动态编程
            • 乞丐不能挑食。他确切地告诉你你应该做什么,你说“做不同的事”,所以他照做了。现在你说他的不同解决方案还不够好。你到底想要什么?源代码?
            • 顺便说一句,树的递归结构肯定指向回溯解决方案。
            猜你喜欢
            • 1970-01-01
            • 2013-09-04
            • 2015-02-15
            • 1970-01-01
            • 2017-08-07
            • 1970-01-01
            • 1970-01-01
            • 2010-09-20
            • 1970-01-01
            相关资源
            最近更新 更多