📜  顶点覆盖问题|集合2(树的动态编程解决方案)

📅  最后修改于: 2021-04-27 22:10:48             🧑  作者: Mango

无向图的顶点覆盖是其顶点的子集,因此对于图的每个边(u,v),“ u”或“ v”都在顶点覆盖中。尽管名称为“顶点覆盖”,但该集合覆盖了给定图形的所有边缘。
寻找图形的最小尺寸顶点覆盖的问题是NP完整的。但这可以在树的多项式时间内解决。在这篇文章中,讨论了二叉树的解决方案。相同的解决方案可以扩展到n元树。

例如,考虑下面的二叉树。最小的顶点覆盖为{20,50,30},顶点覆盖的大小为3。

LargestIndependentSet1

想法是考虑为根遵循以下两种可能性,并为根向下的所有节点递归地遵循以下两种可能性。
1)根是顶点覆盖的一部分:在这种情况下,根覆盖所有子边缘。我们递归地计算左右子树的顶点覆盖大小,并将结果加1(对于根)。

2)根不是顶点覆盖的一部分:在这种情况下,必须将两个根的子代都包括在顶点覆盖中,以覆盖所有根到子边缘。我们递归计算所有孙子代的顶点覆盖大小和结果的子代数(对于两个根的子代)。

以下是上述想法的实现。

C
// A naive recursive C implementation for vertex cover problem for a tree
#include 
#include 
 
// A utility function to find min of two integers
int min(int x, int y) { return (x < y)? x: y; }
 
/* A binary tree node has data, pointer to left child and a pointer to
   right child */
struct node
{
    int data;
    struct node *left, *right;
};
 
// The function returns size of the minimum vertex cover
int vCover(struct node *root)
{
    // The size of minimum vertex cover is zero if tree is empty or there
    // is only one node
    if (root == NULL)
        return 0;
    if (root->left == NULL && root->right == NULL)
        return 0;
 
    // Calculate size of vertex cover when root is part of it
    int size_incl = 1 + vCover(root->left) + vCover(root->right);
 
    // Calculate size of vertex cover when root is not part of it
    int size_excl = 0;
    if (root->left)
      size_excl += 1 + vCover(root->left->left) + vCover(root->left->right);
    if (root->right)
      size_excl += 1 + vCover(root->right->left) + vCover(root->right->right);
 
    // Return the minimum of two sizes
    return min(size_incl, size_excl);
}
 
// A utility function to create a node
struct node* newNode( int data )
{
    struct node* temp = (struct node *) malloc( sizeof(struct node) );
    temp->data = data;
    temp->left = temp->right = NULL;
    return temp;
}
 
// Driver program to test above functions
int main()
{
    // Let us construct the tree given in the above diagram
    struct node *root         = newNode(20);
    root->left                = newNode(8);
    root->left->left          = newNode(4);
    root->left->right         = newNode(12);
    root->left->right->left   = newNode(10);
    root->left->right->right  = newNode(14);
    root->right               = newNode(22);
    root->right->right        = newNode(25);
 
    printf ("Size of the smallest vertex cover is %d ", vCover(root));
 
    return 0;
}


Java
// A naive recursive Java implementation
// for vertex cover problem for a tree
 
class GFG
{
    // A utility function to find min of two integers
    static int min(int x, int y)
    {
        return (x < y) ? x : y;
    }
 
    /*
    * A binary tree node has data, pointer
    to left child and a pointer to right
    * child
    */
    static class node
    {
        int data;
        node left, right;
    };
 
    // The function returns size
    // of the minimum vertex cover
    static int vCover(node root)
    {
        // The size of minimum vertex cover
        // is zero if tree is empty or there
        // is only one node
        if (root == null)
            return 0;
        if (root.left == null && root.right == null)
            return 0;
 
        // Calculate size of vertex cover
        // when root is part of it
        int size_incl = 1 + vCover(root.left) +
                               vCover(root.right);
 
        // Calculate size of vertex cover
        // when root is not part of it
        int size_excl = 0;
        if (root.left != null)
            size_excl += 1 + vCover(root.left.left) +
                              vCover(root.left.right);
        if (root.right != null)
            size_excl += 1 + vCover(root.right.left) +
                                vCover(root.right.right);
 
        // Return the minimum of two sizes
        return Math.min(size_incl, size_excl);
    }
 
    // A utility function to create a node
    static node newNode(int data)
    {
        node temp = new node();
        temp.data = data;
        temp.left = temp.right = null;
        return temp;
    }
 
    // Driver code
    public static void main(String[] args)
    {
        // Let us conthe tree given in the above diagram
        node root = newNode(20);
        root.left = newNode(8);
        root.left.left = newNode(4);
        root.left.right = newNode(12);
        root.left.right.left = newNode(10);
        root.left.right.right = newNode(14);
        root.right = newNode(22);
        root.right.right = newNode(25);
 
        System.out.printf("Size of the smallest vertex" +
                            "cover is %d ", vCover(root));
 
    }
}
 
// This code is contributed by 29AjayKumar


Python3
# A naive recursive Python3 implementation
# for vertex cover problem for a tree
 
# A utility function to find min of two integers
 
# A binary tree node has data, pointer to
# left child and a pointer to right child
class Node:
     
    def __init__(self, x):
         
        self.data = x
        self.left = None
        self.right = None
 
# The function returns size of
# the minimum vertex cover
def vCover(root):
     
    # The size of minimum vertex cover
    # is zero if tree is empty or there
    # is only one node
    if (root == None):
        return 0
         
    if (root.left == None and
       root.right == None):
        return 0
 
    # Calculate size of vertex cover when
    # root is part of it
    size_incl = (1 + vCover(root.left) +
                     vCover(root.right))
 
    # Calculate size of vertex cover
    # when root is not part of it
    size_excl = 0
    if (root.left):
      size_excl += (1 + vCover(root.left.left) +
                        vCover(root.left.right))
    if (root.right):
      size_excl += (1 + vCover(root.right.left) +
                        vCover(root.right.right))
 
    # Return the minimum of two sizes
    return min(size_incl, size_excl)
 
# Driver Code
if __name__ == '__main__':
     
    # Let us construct the tree
    # given in the above diagram
    root  = Node(20)
    root.left = Node(8)
    root.left.left = Node(4)
    root.left.right = Node(12)
    root.left.right.left = Node(10)
    root.left.right.right = Node(14)
    root.right  = Node(22)
    root.right.right = Node(25)
 
    print("Size of the smallest vertex cover is", vCover(root))
 
# This code is contributed by mohit kumar 29


C#
// A naive recursive C# implementation
// for vertex cover problem for a tree
using System;
 
class GFG
{
    // A utility function to find
    // min of two integers
    static int min(int x, int y)
    {
        return (x < y) ? x : y;
    }
 
    /*
    * A binary tree node has data, pointer
    to left child and a pointer to right
    * child
    */
    public class node
    {
        public int data;
        public node left, right;
    };
 
    // The function returns size
    // of the minimum vertex cover
    static int vCover(node root)
    {
        // The size of minimum vertex cover
        // is zero if tree is empty or there
        // is only one node
        if (root == null)
            return 0;
        if (root.left == null &&
            root.right == null)
            return 0;
 
        // Calculate size of vertex cover
        // when root is part of it
        int size_incl = 1 + vCover(root.left) +
                            vCover(root.right);
 
        // Calculate size of vertex cover
        // when root is not part of it
        int size_excl = 0;
        if (root.left != null)
            size_excl += 1 + vCover(root.left.left) +
                             vCover(root.left.right);
        if (root.right != null)
            size_excl += 1 + vCover(root.right.left) +
                             vCover(root.right.right);
 
        // Return the minimum of two sizes
        return Math.Min(size_incl, size_excl);
    }
 
    // A utility function to create a node
    static node newNode(int data)
    {
        node temp = new node();
        temp.data = data;
        temp.left = temp.right = null;
        return temp;
    }
 
    // Driver code
    public static void Main(String[] args)
    {
        // Let us conthe tree given
        // in the above diagram
        node root = newNode(20);
        root.left = newNode(8);
        root.left.left = newNode(4);
        root.left.right = newNode(12);
        root.left.right.left = newNode(10);
        root.left.right.right = newNode(14);
        root.right = newNode(22);
        root.right.right = newNode(25);
 
        Console.Write("Size of the smallest vertex" +
                      "cover is {0} ", vCover(root));
    }
}
 
// This code is contributed by 29AjayKumar


C
/* Dynamic programming based program for Vertex Cover problem for
   a Binary Tree */
#include 
#include 
 
// A utility function to find min of two integers
int min(int x, int y) { return (x < y)? x: y; }
 
/* A binary tree node has data, pointer to left child and a pointer to
   right child */
struct node
{
    int data;
    int vc;
    struct node *left, *right;
};
 
// A memoization based function that returns size of the minimum vertex cover.
int vCover(struct node *root)
{
    // The size of minimum vertex cover is zero if tree is empty or there
    // is only one node
    if (root == NULL)
        return 0;
    if (root->left == NULL && root->right == NULL)
        return 0;
 
    // If vertex cover for this node is already evaluated, then return it
    // to save recomputation of same subproblem again.
    if (root->vc != 0)
        return root->vc;
 
    // Calculate size of vertex cover when root is part of it
    int size_incl = 1 + vCover(root->left) + vCover(root->right);
 
    // Calculate size of vertex cover when root is not part of it
    int size_excl = 0;
    if (root->left)
      size_excl += 1 + vCover(root->left->left) + vCover(root->left->right);
    if (root->right)
      size_excl += 1 + vCover(root->right->left) + vCover(root->right->right);
 
    // Minimum of two values is vertex cover, store it before returning
    root->vc =  min(size_incl, size_excl);
 
    return root->vc;
}
 
// A utility function to create a node
struct node* newNode( int data )
{
    struct node* temp = (struct node *) malloc( sizeof(struct node) );
    temp->data = data;
    temp->left = temp->right = NULL;
    temp->vc = 0; // Set the vertex cover as 0
    return temp;
}
 
// Driver program to test above functions
int main()
{
    // Let us construct the tree given in the above diagram
    struct node *root         = newNode(20);
    root->left                = newNode(8);
    root->left->left          = newNode(4);
    root->left->right         = newNode(12);
    root->left->right->left   = newNode(10);
    root->left->right->right  = newNode(14);
    root->right               = newNode(22);
    root->right->right        = newNode(25);
 
    printf ("Size of the smallest vertex cover is %d ", vCover(root));
 
    return 0;
}


Java
/* Dynamic programming based program for
Vertex Cover problem for a Binary Tree */
 
class GFG
{
    // A utility function to find min of two integers
    static int min(int x, int y)
    {
        return (x < y) ? x : y;
    }
 
    /*
    * A binary tree node has data, pointer
    to left child and a pointer to right
    * child
    */
    static class node
    {
        int data;
        int vc;
        node left, right;
    };
 
    // A memoization based function that returns
    // size of the minimum vertex cover.
    static int vCover(node root)
    {
        // The size of minimum vertex cover is zero
        //  if tree is empty or there is only one node
        if (root == null)
            return 0;
        if (root.left == null && root.right == null)
            return 0;
 
        // If vertex cover for this node is
        // already evaluated, then return it
        // to save recomputation of same subproblem again.
        if (root.vc != 0)
            return root.vc;
 
        // Calculate size of vertex cover
        // when root is part of it
        int size_incl = 1 + vCover(root.left) +
                            vCover(root.right);
 
        // Calculate size of vertex cover
        // when root is not part of it
        int size_excl = 0;
        if (root.left != null)
            size_excl += 1 + vCover(root.left.left) +
                                vCover(root.left.right);
        if (root.right != null)
            size_excl += 1 + vCover(root.right.left) +
                                vCover(root.right.right);
 
        // Minimum of two values is vertex cover,
        // store it before returning
        root.vc = Math.min(size_incl, size_excl);
 
        return root.vc;
    }
 
    // A utility function to create a node
    static node newNode(int data)
    {
        node temp = new node();
        temp.data = data;
        temp.left = temp.right = null;
        temp.vc = 0; // Set the vertex cover as 0
        return temp;
    }
 
    // Driver code
    public static void main(String[] args)
    {
        // Let us conthe tree given in the above diagram
        node root = newNode(20);
        root.left = newNode(8);
        root.left.left = newNode(4);
        root.left.right = newNode(12);
        root.left.right.left = newNode(10);
        root.left.right.right = newNode(14);
        root.right = newNode(22);
        root.right.right = newNode(25);
 
        System.out.printf("Size of the smallest vertex" +
                            "cover is %d ", vCover(root));
    }
}
 
// This code is contributed by PrinciRaj1992


C#
/* Dynamic programming based program for
Vertex Cover problem for a Binary Tree */
using System;
 
class GFG
{
    // A utility function to find
    // min of two integers
    static int min(int x, int y)
    {
        return (x < y) ? x : y;
    }
 
    /*
    * A binary tree node has data, pointer
    to left child and a pointer to right
    * child
    */
    class node
    {
        public int data;
        public int vc;
        public node left, right;
    };
 
    // A memoization based function that returns
    // size of the minimum vertex cover.
    static int vCover(node root)
    {
        // The size of minimum vertex cover is zero
        // if tree is empty or there is only one node
        if (root == null)
            return 0;
        if (root.left == null &&
            root.right == null)
            return 0;
 
        // If vertex cover for this node is
        // already evaluated, then return it
        // to save recomputation of same subproblem again.
        if (root.vc != 0)
            return root.vc;
 
        // Calculate size of vertex cover
        // when root is part of it
        int size_incl = 1 + vCover(root.left) +
                            vCover(root.right);
 
        // Calculate size of vertex cover
        // when root is not part of it
        int size_excl = 0;
        if (root.left != null)
            size_excl += 1 + vCover(root.left.left) +
                             vCover(root.left.right);
        if (root.right != null)
            size_excl += 1 + vCover(root.right.left) +
                             vCover(root.right.right);
 
        // Minimum of two values is vertex cover,
        // store it before returning
        root.vc = Math.Min(size_incl, size_excl);
 
        return root.vc;
    }
 
    // A utility function to create a node
    static node newNode(int data)
    {
        node temp = new node();
        temp.data = data;
        temp.left = temp.right = null;
        temp.vc = 0; // Set the vertex cover as 0
        return temp;
    }
 
    // Driver code
    public static void Main(String[] args)
    {
        // Let us conthe tree given in the above diagram
        node root = newNode(20);
        root.left = newNode(8);
        root.left.left = newNode(4);
        root.left.right = newNode(12);
        root.left.right.left = newNode(10);
        root.left.right.right = newNode(14);
        root.right = newNode(22);
        root.right.right = newNode(25);
 
        Console.Write("Size of the smallest vertex" +
                      "cover is {0} ", vCover(root));
    }
}
 
// This code is contributed by PrinciRaj1992


输出:

Size of the smallest vertex cover is 3

上述幼稚递归方法的时间复杂度是指数的。应该注意的是,上述函数一次又一次地计算相同的子问题。例如,值为50的节点的vCover评估两次,因为50的孙子为10,子项为20。

由于再次调用了相同的问题,因此此问题具有“重叠子问题”属性。因此,“顶点覆盖”问题具有动态编程问题的两个属性(请参阅此内容)。像其他典型的动态规划(DP)问题一样,可以通过存储子问题的解决方案并以自下而上的方式解决问题,避免相同子问题的重新计算。

以下是基于动态编程的解决方案的实现。在以下解决方案中,一个附加字段’vc’被添加到树节点。对于所有节点,“ vc”的初始值均设置为0。递归函数vCover()仅在尚未设置节点时才为其计算“ vc”。

C

/* Dynamic programming based program for Vertex Cover problem for
   a Binary Tree */
#include 
#include 
 
// A utility function to find min of two integers
int min(int x, int y) { return (x < y)? x: y; }
 
/* A binary tree node has data, pointer to left child and a pointer to
   right child */
struct node
{
    int data;
    int vc;
    struct node *left, *right;
};
 
// A memoization based function that returns size of the minimum vertex cover.
int vCover(struct node *root)
{
    // The size of minimum vertex cover is zero if tree is empty or there
    // is only one node
    if (root == NULL)
        return 0;
    if (root->left == NULL && root->right == NULL)
        return 0;
 
    // If vertex cover for this node is already evaluated, then return it
    // to save recomputation of same subproblem again.
    if (root->vc != 0)
        return root->vc;
 
    // Calculate size of vertex cover when root is part of it
    int size_incl = 1 + vCover(root->left) + vCover(root->right);
 
    // Calculate size of vertex cover when root is not part of it
    int size_excl = 0;
    if (root->left)
      size_excl += 1 + vCover(root->left->left) + vCover(root->left->right);
    if (root->right)
      size_excl += 1 + vCover(root->right->left) + vCover(root->right->right);
 
    // Minimum of two values is vertex cover, store it before returning
    root->vc =  min(size_incl, size_excl);
 
    return root->vc;
}
 
// A utility function to create a node
struct node* newNode( int data )
{
    struct node* temp = (struct node *) malloc( sizeof(struct node) );
    temp->data = data;
    temp->left = temp->right = NULL;
    temp->vc = 0; // Set the vertex cover as 0
    return temp;
}
 
// Driver program to test above functions
int main()
{
    // Let us construct the tree given in the above diagram
    struct node *root         = newNode(20);
    root->left                = newNode(8);
    root->left->left          = newNode(4);
    root->left->right         = newNode(12);
    root->left->right->left   = newNode(10);
    root->left->right->right  = newNode(14);
    root->right               = newNode(22);
    root->right->right        = newNode(25);
 
    printf ("Size of the smallest vertex cover is %d ", vCover(root));
 
    return 0;
}

Java

/* Dynamic programming based program for
Vertex Cover problem for a Binary Tree */
 
class GFG
{
    // A utility function to find min of two integers
    static int min(int x, int y)
    {
        return (x < y) ? x : y;
    }
 
    /*
    * A binary tree node has data, pointer
    to left child and a pointer to right
    * child
    */
    static class node
    {
        int data;
        int vc;
        node left, right;
    };
 
    // A memoization based function that returns
    // size of the minimum vertex cover.
    static int vCover(node root)
    {
        // The size of minimum vertex cover is zero
        //  if tree is empty or there is only one node
        if (root == null)
            return 0;
        if (root.left == null && root.right == null)
            return 0;
 
        // If vertex cover for this node is
        // already evaluated, then return it
        // to save recomputation of same subproblem again.
        if (root.vc != 0)
            return root.vc;
 
        // Calculate size of vertex cover
        // when root is part of it
        int size_incl = 1 + vCover(root.left) +
                            vCover(root.right);
 
        // Calculate size of vertex cover
        // when root is not part of it
        int size_excl = 0;
        if (root.left != null)
            size_excl += 1 + vCover(root.left.left) +
                                vCover(root.left.right);
        if (root.right != null)
            size_excl += 1 + vCover(root.right.left) +
                                vCover(root.right.right);
 
        // Minimum of two values is vertex cover,
        // store it before returning
        root.vc = Math.min(size_incl, size_excl);
 
        return root.vc;
    }
 
    // A utility function to create a node
    static node newNode(int data)
    {
        node temp = new node();
        temp.data = data;
        temp.left = temp.right = null;
        temp.vc = 0; // Set the vertex cover as 0
        return temp;
    }
 
    // Driver code
    public static void main(String[] args)
    {
        // Let us conthe tree given in the above diagram
        node root = newNode(20);
        root.left = newNode(8);
        root.left.left = newNode(4);
        root.left.right = newNode(12);
        root.left.right.left = newNode(10);
        root.left.right.right = newNode(14);
        root.right = newNode(22);
        root.right.right = newNode(25);
 
        System.out.printf("Size of the smallest vertex" +
                            "cover is %d ", vCover(root));
    }
}
 
// This code is contributed by PrinciRaj1992

C#

/* Dynamic programming based program for
Vertex Cover problem for a Binary Tree */
using System;
 
class GFG
{
    // A utility function to find
    // min of two integers
    static int min(int x, int y)
    {
        return (x < y) ? x : y;
    }
 
    /*
    * A binary tree node has data, pointer
    to left child and a pointer to right
    * child
    */
    class node
    {
        public int data;
        public int vc;
        public node left, right;
    };
 
    // A memoization based function that returns
    // size of the minimum vertex cover.
    static int vCover(node root)
    {
        // The size of minimum vertex cover is zero
        // if tree is empty or there is only one node
        if (root == null)
            return 0;
        if (root.left == null &&
            root.right == null)
            return 0;
 
        // If vertex cover for this node is
        // already evaluated, then return it
        // to save recomputation of same subproblem again.
        if (root.vc != 0)
            return root.vc;
 
        // Calculate size of vertex cover
        // when root is part of it
        int size_incl = 1 + vCover(root.left) +
                            vCover(root.right);
 
        // Calculate size of vertex cover
        // when root is not part of it
        int size_excl = 0;
        if (root.left != null)
            size_excl += 1 + vCover(root.left.left) +
                             vCover(root.left.right);
        if (root.right != null)
            size_excl += 1 + vCover(root.right.left) +
                             vCover(root.right.right);
 
        // Minimum of two values is vertex cover,
        // store it before returning
        root.vc = Math.Min(size_incl, size_excl);
 
        return root.vc;
    }
 
    // A utility function to create a node
    static node newNode(int data)
    {
        node temp = new node();
        temp.data = data;
        temp.left = temp.right = null;
        temp.vc = 0; // Set the vertex cover as 0
        return temp;
    }
 
    // Driver code
    public static void Main(String[] args)
    {
        // Let us conthe tree given in the above diagram
        node root = newNode(20);
        root.left = newNode(8);
        root.left.left = newNode(4);
        root.left.right = newNode(12);
        root.left.right.left = newNode(10);
        root.left.right.right = newNode(14);
        root.right = newNode(22);
        root.right.right = newNode(25);
 
        Console.Write("Size of the smallest vertex" +
                      "cover is {0} ", vCover(root));
    }
}
 
// This code is contributed by PrinciRaj1992

输出:

Size of the smallest vertex cover is 3