无向图的顶点覆盖是其顶点的子集,因此对于图的每条边 (u, v),“u”或“v”都在顶点覆盖中。尽管名称是 Vertex Cover,但该集合涵盖了给定图的所有边。
找到图的最小尺寸顶点覆盖的问题是 NP 完全的。但它可以在多项式时间内解决树。在这篇文章中,讨论了二叉树的解决方案。相同的解决方案可以扩展到 n 叉树。
例如,考虑以下二叉树。最小的顶点覆盖为 {20, 50, 30},顶点覆盖的大小为 3。
这个想法是考虑根的以下两种可能性,以及根下所有节点的递归。
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 construct 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 construct 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
Javascript
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 construct 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 construct 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
Javascript
C++
// C++ implementation for the above approach
#include
using namespace std;
// An utility function to add an edge in the tree
void addEdge(vector adj[], int x, int y)
{
adj[x].push_back(y);
adj[y].push_back(x);
}
void dfs(vector adj[], vector dp[], int src,
int par)
{
for (auto child : adj[src]) {
if (child != par)
dfs(adj, dp, child, src);
}
for (auto child : adj[src]) {
if (child != par) {
// not including source in the vertex cover
dp[src][0] += dp[child][1];
// including source in the vertex cover
dp[src][1] += min(dp[child][1], dp[child][0]);
}
}
}
// function to find minimum size of vertex cover
void minSizeVertexCover(vector adj[], int N)
{
vector dp[N + 1];
for (int i = 1; i <= N; i++) {
// 0 denotes not included in vertex cover
dp[i].push_back(0);
// 1 denotes included in vertex cover
dp[i].push_back(1);
}
dfs(adj, dp, 1, -1);
// printing minimum size vertex cover
cout << min(dp[1][0], dp[1][1]) << endl;
}
// Driver Code
int main()
{
/* 1
/ \
2 7
/ \
3 6
/ | \
4 8 5
*/
// number of nodes in the tree
int N = 8;
// adjacency list representation of the tree
vector adj[N + 1];
addEdge(adj, 1, 2);
addEdge(adj, 1, 7);
addEdge(adj, 2, 3);
addEdge(adj, 2, 6);
addEdge(adj, 3, 4);
addEdge(adj, 3, 8);
addEdge(adj, 3, 5);
minSizeVertexCover(adj, N);
return 0;
}
输出:
Size of the smallest vertex cover is 3
上述朴素递归方法的时间复杂度是指数级的。需要注意的是,上述函数一次又一次地计算相同的子问题。例如,值为 50 的节点的 vCover 被评估两次,因为 50 是 10 的孙子和 20 的子节点。
由于再次调用相同的子问题,因此该问题具有重叠子问题的属性。所以顶点覆盖问题具有动态规划问题的两个属性(见this和this)。与其他典型的动态规划 (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 construct 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 construct 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
Javascript
输出:
Size of the smallest vertex cover is 3
任何一般树的方法:
1. 方法将与所讨论的动态规划方法相同。
2.对于每个节点,如果我们从顶点覆盖中排除这个节点,那么我们必须包括它的相邻节点,
如果我们将这个节点包含在顶点覆盖中,那么我们将取其相邻的两种可能性中的最小值
顶点覆盖中的节点以获得最小顶点覆盖。
3.我们将以上信息存储在dp数组中。
C++
// C++ implementation for the above approach
#include
using namespace std;
// An utility function to add an edge in the tree
void addEdge(vector adj[], int x, int y)
{
adj[x].push_back(y);
adj[y].push_back(x);
}
void dfs(vector adj[], vector dp[], int src,
int par)
{
for (auto child : adj[src]) {
if (child != par)
dfs(adj, dp, child, src);
}
for (auto child : adj[src]) {
if (child != par) {
// not including source in the vertex cover
dp[src][0] += dp[child][1];
// including source in the vertex cover
dp[src][1] += min(dp[child][1], dp[child][0]);
}
}
}
// function to find minimum size of vertex cover
void minSizeVertexCover(vector adj[], int N)
{
vector dp[N + 1];
for (int i = 1; i <= N; i++) {
// 0 denotes not included in vertex cover
dp[i].push_back(0);
// 1 denotes included in vertex cover
dp[i].push_back(1);
}
dfs(adj, dp, 1, -1);
// printing minimum size vertex cover
cout << min(dp[1][0], dp[1][1]) << endl;
}
// Driver Code
int main()
{
/* 1
/ \
2 7
/ \
3 6
/ | \
4 8 5
*/
// number of nodes in the tree
int N = 8;
// adjacency list representation of the tree
vector adj[N + 1];
addEdge(adj, 1, 2);
addEdge(adj, 1, 7);
addEdge(adj, 2, 3);
addEdge(adj, 2, 6);
addEdge(adj, 3, 4);
addEdge(adj, 3, 8);
addEdge(adj, 3, 5);
minSizeVertexCover(adj, N);
return 0;
}
3
时间复杂度: O(N)
辅助空间: O(N)
如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程和学生竞争性编程现场课程。