📜  使用二分搜索查找图的最小顶点覆盖大小

📅  最后修改于: 2022-05-13 01:57:54.421000             🧑  作者: Mango

使用二分搜索查找图的最小顶点覆盖大小

无向图的顶点覆盖是其顶点的子集,因此对于图的每条边 (u, v),“u”或“v”都在顶点覆盖中。一个图可能有很多顶点覆盖。

问题对于具有 V 个顶点和 m 个边的无向连通图,找到最小尺寸顶点覆盖的大小,即具有最小基数的顶点覆盖的基数。

例子:

Input: V = 6, E = 6
             6
                /
           /
          1 -----5
         /|\
        3 | \
        \ |  \
          2   4
Output: Minimum vertex cover size = 2
Consider subset of vertices {1, 2}, every edge 
in above graph is either incident on vertex 1 
or 2. Hence the minimum vertex cover = {1, 2}, 
the size of which is 2.

Input: V = 6, E = 7
           2 ---- 4 ---- 6
          /|      |
        1  |      |
          \|      |
           3 ---- 5
Output: Minimum vertex cover size = 3
Consider subset of vertices {2, 3, 4}, every
edge in above graph is either incident on 
vertex 2, 3 or 4. Hence the minimum size of
a vertex cover can be 3.

方法1(天真)
如果给定的顶点子集是否是顶点覆盖,我们可以使用以下算法检查 O(E + V) 时间。

Generate all 2V subsets of vertices in graph and
do following for every subset.
  1. edges_covered = 0
  2. for each vertex in current subset
  3.   for all edges emerging out of current vertex
  4.      if the edge is not already marked visited
  5.          mark the edge visited
  6.          edges_covered++
  7. if edges_covered is equal to total number edges
  8.   return size of current subset

此解决方案的时间复杂度上限为 O((E + V) * 2 V )

方法 2(二分查找)
如果我们首先通过生成V C V子集来生成 2 V子集,然后生成V C (V-1)子集,依此类推,直到V C 0个子集(2 V = V C V + V C (V-1) + ... + V C 1 + V C 0 )。我们现在的目标是找到最小 k 使得V C k个子集中至少一个大小为“k”的子集是一个顶点覆盖[我们知道,如果最小大小的顶点覆盖的大小为 k,那么将存在一个顶点覆盖大于 k 的所有尺寸。也就是说,将有一个大小为 k + 1, k + 2, k + 3, ..., n 的顶点覆盖。 ]

现在让我们想象一个大小为 n 的布尔数组并将其称为 isCover[]。因此,如果问题的答案; “是否存在大小为 x 的顶点覆盖?”是的,我们在第 x 个位置放一个“1”,否则为“0”。

数组 isCover[] 看起来像:

123...k...n
000...1...1

该数组已排序,因此可进行二进制搜索,因为k之前的任何索引都不会有“1”,而k (包括)之后的每个索引都会有“1”,所以k就是答案。

所以我们可以应用二分查找来找到覆盖所有边的最小尺寸的顶点集(这个问题相当于在isCover[]中找到last 1)。现在的问题是如何生成给定大小的所有子集。这个想法是使用 Gosper 的 hack。

什么是 Gosper 的黑客?
Gosper 的 hack 是一种获得具有相同位数设置的下一个数字的技术。因此,我们从右边开始设置前 x 位并生成下一个设置 x 位的数字,直到该数字小于 2 V 。这样,我们可以生成所有设置了 x 位的V Cx 数。

int set = (1 << k) - 1;
int limit = (1 << V);
while (set < limit) 
{
    // Do your stuff with current set
    doStuff(set);
  
    // Gosper's hack:
    int c = set & -set;
    int r = set + c;
    set = (((r^set) >>> 2) / c) | r;
}

资料来源:堆栈交换

我们使用 gosper 的 hack 来生成大小为 x(0 < x <= V) 的所有子集,即检查我们在 isCover[] 数组中的任何索引 x 处是否有“1”或“0”。

C++
// A C++ program to find size of minimum vertex
// cover using Binary Search
#include
#define maxn 25
  
using namespace std;
  
// Global array to store the graph
// Note: since the array is global, all the
// elements are 0 initially
bool gr[maxn][maxn];
  
// Returns true if there is a possible subset
// of size 'k' that can be a vertex cover
bool isCover(int V, int k, int E)
{
    // Set has first 'k' bits high initially
    int set = (1 << k) - 1;
  
    int limit = (1 << V);
  
    // to mark the edges covered in each subset
    // of size 'k'
    bool vis[maxn][maxn];
  
    while (set < limit)
    {
        // Reset visited array for every subset
        // of vertices
        memset(vis, 0, sizeof vis);
  
        // set counter for number of edges covered
        // from this subset of vertices to zero
        int cnt = 0;
  
        // selected vertex cover is the indices
        // where 'set' has its bit high
        for (int j = 1, v = 1 ; j < limit ; j = j << 1, v++)
        {
            if (set & j)
            {
                // Mark all edges emerging out of this
                // vertex visited
                for (int k = 1 ; k <= V ; k++)
                {
                    if (gr[v][k] && !vis[v][k])
                    {
                        vis[v][k] = 1;
                        vis[k][v] = 1;
                        cnt++;
                    }
                }
            }
        }
  
        // If the current subset covers all the edges
        if (cnt == E)
            return true;
  
        // Generate previous combination with k bits high
        // set & -set = (1 << last bit high in set)
        int c = set & -set;
        int r = set + c;
        set = (((r^set) >> 2) / c) | r;
    }
    return false;
}
  
// Returns answer to graph stored in gr[][]
int findMinCover(int n, int m)
{
    // Binary search the answer
    int left = 1, right = n;
    while (right > left)
    {
        int mid = (left + right) >> 1;
        if (isCover(n, mid, m) == false)
            left = mid + 1;
        else
            right = mid;
    }
  
    // at the end of while loop both left and
    // right will be equal,/ as when they are
    // not, the while loop won't exit the minimum
    // size vertex cover = left = right
    return left;
}
  
// Inserts an edge in the graph
void insertEdge(int u, int v)
{
    gr[u][v] = 1;
    gr[v][u] = 1;  // Undirected graph
}
  
// Driver code
int main()
{
    /*
            6
           /
          1 ----- 5   vertex cover = {1, 2}
         /|\
        3 | \
        \ |  \
          2   4   */
    int V = 6, E = 6;
    insertEdge(1, 2);
    insertEdge(2, 3);
    insertEdge(1, 3);
    insertEdge(1, 4);
    insertEdge(1, 5);
    insertEdge(1, 6);
    cout << "Minimum size of a vertex cover = "
         << findMinCover(V, E) << endl;
  
  
    // Let us create another graph
    memset(gr, 0, sizeof gr);
    /*
           2 ---- 4 ---- 6
          /|      |
        1  |      |   vertex cover = {2, 3, 4}
         \ |      |
           3 ---- 5    */
  
    V = 6, E = 7;
    insertEdge(1, 2);
    insertEdge(1, 3);
    insertEdge(2, 3);
    insertEdge(2, 4);
    insertEdge(3, 5);
    insertEdge(4, 5);
    insertEdge(4, 6);
    cout << "Minimum size of a vertex cover = "
         << findMinCover(V, E) << endl;
  
    return 0;
}


Java
// A Java program to find size of minimum vertex
// cover using Binary Search
class GFG
{
  
static final int maxn = 25;
  
// Global array to store the graph
// Note: since the array is global, all the
// elements are 0 initially
static boolean [][]gr = new boolean[maxn][maxn];
  
// Returns true if there is a possible subset
// of size 'k' that can be a vertex cover
static boolean isCover(int V, int k, int E)
{
    // Set has first 'k' bits high initially
    int set = (1 << k) - 1;
  
    int limit = (1 << V);
  
    // to mark the edges covered in each subset
    // of size 'k'
    boolean [][]vis = new boolean[maxn][maxn];;
  
    while (set < limit)
    {
        // Reset visited array for every subset
        // of vertices
        for(int i = 0; i < maxn; i++)
        {
            for(int j = 0; j < maxn; j++)
            {
                vis[i][j] = false;
            }
        }
        // set counter for number of edges covered
        // from this subset of vertices to zero
        int cnt = 0;
  
        // selected vertex cover is the indices
        // where 'set' has its bit high
        for (int j = 1, v = 1 ; j < limit ; j = j << 1, v++)
        {
            if ((set & j) != 0)
            {
                // Mark all edges emerging out of this
                // vertex visited
                for (int co = 1 ; co <= V ; co++)
                {
                    if (gr[v][co] && !vis[v][co])
                    {
                        vis[v][co] = true;
                        vis[co][v] = true;
                        cnt++;
                    }
                }
            }
        }
  
        // If the current subset covers all the edges
        if (cnt == E)
            return true;
  
        // Generate previous combination with k bits high
        // set & -set = (1 << last bit high in set)
        int co = set & -set;
        int ro = set + co;
        set = (((ro^set) >> 2) / co) | ro;
    }
    return false;
}
  
// Returns answer to graph stored in gr[][]
static int findMinCover(int n, int m)
{
    // Binary search the answer
    int left = 1, right = n;
    while (right > left)
    {
        int mid = (left + right) >> 1;
        if (isCover(n, mid, m) == false)
            left = mid + 1;
        else
            right = mid;
    }
  
    // at the end of while loop both left and
    // right will be equal,/ as when they are
    // not, the while loop won't exit the minimum
    // size vertex cover = left = right
    return left;
}
  
// Inserts an edge in the graph
static void insertEdge(int u, int v)
{
    gr[u][v] = true;
    gr[v][u] = true; // Undirected graph
}
  
// Driver code
public static void main(String[] args)
{
    /*
            6
        /
        1 ----- 5 vertex cover = {1, 2}
        /|\
        3 | \
        \ | \
        2 4 */
    int V = 6, E = 6;
    insertEdge(1, 2);
    insertEdge(2, 3);
    insertEdge(1, 3);
    insertEdge(1, 4);
    insertEdge(1, 5);
    insertEdge(1, 6);
    System.out.print("Minimum size of a vertex cover = "
        + findMinCover(V, E) +"\n");
  
  
    // Let us create another graph
    for(int i = 0; i < maxn; i++)
    {
        for(int j = 0; j < maxn; j++)
        {
            gr[i][j] = false;
        }
    }
    /*
        2 ---- 4 ---- 6
        /|     |
        1 |     | vertex cover = {2, 3, 4}
        \ |     |
        3 ---- 5 */
  
    V = 6;
    E = 7;
    insertEdge(1, 2);
    insertEdge(1, 3);
    insertEdge(2, 3);
    insertEdge(2, 4);
    insertEdge(3, 5);
    insertEdge(4, 5);
    insertEdge(4, 6);
    System.out.print("Minimum size of a vertex cover = "
        + findMinCover(V, E) +"\n");
  
}
}
  
// This code is contributed by Rajput-Ji


Python3
# A Python3 program to find size of minimum 
# vertex cover using Binary Search
  
# Returns true if there is a possible subSet 
# of size 'k' that can be a vertex cover 
def isCover(V, k, E):
      
    # Set has first 'k' bits high initially 
    Set = (1 << k) - 1
  
    limit = (1 << V) 
  
    # to mark the edges covered in each 
    # subSet of size 'k' 
    vis = [[None] * maxn for i in range(maxn)] 
  
    while (Set < limit):
          
        # ReSet visited array for every 
        # subSet of vertices 
        vis = [[0] * maxn for i in range(maxn)] 
  
        # Set counter for number of edges covered 
        # from this subSet of vertices to zero 
        cnt = 0
  
        # selected vertex cover is the 
        # indices where 'Set' has its bit high
        j = 1
        v = 1
        while(j < limit):
            if (Set & j):
                  
                # Mark all edges emerging out of  
                # this vertex visited
                for k in range(1, V + 1):
                    if (gr[v][k] and not vis[v][k]):
                        vis[v][k] = 1
                        vis[k][v] = 1
                        cnt += 1
            j = j << 1
            v += 1
  
        # If the current subSet covers all the edges 
        if (cnt == E): 
            return True
  
        # Generate previous combination with k bits high 
        # Set & -Set = (1 << last bit high in Set) 
        c = Set & -Set
        r = Set + c 
        Set = (((r ^ Set) >> 2) // c) | r
    return False
  
# Returns answer to graph stored in gr[][] 
def findMinCover(n, m):
      
    # Binary search the answer 
    left = 1
    right = n 
    while (right > left):
        mid = (left + right) >> 1
        if (isCover(n, mid, m) == False): 
            left = mid + 1
        else:
            right = mid
  
    # at the end of while loop both left and 
    # right will be equal,/ as when they are 
    # not, the while loop won't exit the 
    # minimum size vertex cover = left = right 
    return left
  
# Inserts an edge in the graph 
def insertEdge(u, v):
    gr[u][v] = 1
    gr[v][u] = 1 # Undirected graph
  
# Driver code 
maxn = 25
  
# Global array to store the graph 
# Note: since the array is global,  
# all the elements are 0 initially 
gr = [[None] * maxn for i in range(maxn)] 
  
# 
#     6 
#     / 
#     1 ----- 5 vertex cover = {1, 2} 
#     /|\ 
# 3 | \ 
# \ | \ 
#     2 4 
V = 6
E = 6
insertEdge(1, 2) 
insertEdge(2, 3) 
insertEdge(1, 3) 
insertEdge(1, 4) 
insertEdge(1, 5) 
insertEdge(1, 6) 
print("Minimum size of a vertex cover = ",
                       findMinCover(V, E))
  
# Let us create another graph 
gr = [[0] * maxn for i in range(maxn)] 
  
# 
#     2 ---- 4 ---- 6 
#     /|     | 
# 1 |     | vertex cover = {2, 3, 4} 
#     \ |     | 
#     3 ---- 5 
V = 6
E = 7
insertEdge(1, 2) 
insertEdge(1, 3) 
insertEdge(2, 3) 
insertEdge(2, 4) 
insertEdge(3, 5) 
insertEdge(4, 5) 
insertEdge(4, 6) 
print("Minimum size of a vertex cover = ",
                       findMinCover(V, E))
  
# This code is contributed by PranchalK


C#
// A C# program to find size of minimum vertex
// cover using Binary Search
using System;
  
class GFG
{
  
static readonly int maxn = 25;
  
// Global array to store the graph
// Note: since the array is global, all the
// elements are 0 initially
static bool [,]gr = new bool[maxn, maxn];
  
// Returns true if there is a possible subset
// of size 'k' that can be a vertex cover
static bool isCover(int V, int k, int E)
{
    // Set has first 'k' bits high initially
    int set = (1 << k) - 1;
  
    int limit = (1 << V);
  
    // to mark the edges covered in each subset
    // of size 'k'
    bool [,]vis = new bool[maxn, maxn];;
  
    while (set < limit)
    {
        // Reset visited array for every subset
        // of vertices
        for(int i = 0; i < maxn; i++)
        {
            for(int j = 0; j < maxn; j++)
            {
                vis[i, j] = false;
            }
        }
          
        // set counter for number of edges covered
        // from this subset of vertices to zero
        int cnt = 0;
  
        // selected vertex cover is the indices
        // where 'set' has its bit high
        for (int j = 1, v = 1 ; j < limit ; j = j << 1, v++)
        {
            if ((set & j) != 0)
            {
                // Mark all edges emerging out of this
                // vertex visited
                for (int co = 1 ; co <= V ; co++)
                {
                    if (gr[v, co] && !vis[v, co])
                    {
                        vis[v, co] = true;
                        vis[co, v] = true;
                        cnt++;
                    }
                }
            }
        }
  
        // If the current subset covers all the edges
        if (cnt == E)
            return true;
  
        // Generate previous combination with k bits high
        // set & -set = (1 << last bit high in set)
        int cO = set & -set;
        int rO = set + cO;
        set = (((rO^set) >> 2) / cO) | rO;
    }
    return false;
}
  
// Returns answer to graph stored in gr[,]
static int findMinCover(int n, int m)
{
    // Binary search the answer
    int left = 1, right = n;
    while (right > left)
    {
        int mid = (left + right) >> 1;
        if (isCover(n, mid, m) == false)
            left = mid + 1;
        else
            right = mid;
    }
  
    // at the end of while loop both left and
    // right will be equal,/ as when they are
    // not, the while loop won't exit the minimum
    // size vertex cover = left = right
    return left;
}
  
// Inserts an edge in the graph
static void insertEdge(int u, int v)
{
    gr[u, v] = true;
    gr[v, u] = true; // Undirected graph
}
  
// Driver code
public static void Main(String[] args)
{
    /*
            6
        /
        1 ----- 5 vertex cover = {1, 2}
        /|\
        3 | \
        \ | \
        2 4 */
    int V = 6, E = 6;
    insertEdge(1, 2);
    insertEdge(2, 3);
    insertEdge(1, 3);
    insertEdge(1, 4);
    insertEdge(1, 5);
    insertEdge(1, 6);
    Console.Write("Minimum size of a vertex cover = "
        + findMinCover(V, E) +"\n");
  
  
    // Let us create another graph
    for(int i = 0; i < maxn; i++)
    {
        for(int j = 0; j < maxn; j++)
        {
            gr[i,j] = false;
        }
    }
    /*
        2 ---- 4 ---- 6
        /|     |
        1 |     | vertex cover = {2, 3, 4}
        \ |     |
        3 ---- 5 */
  
    V = 6;
    E = 7;
    insertEdge(1, 2);
    insertEdge(1, 3);
    insertEdge(2, 3);
    insertEdge(2, 4);
    insertEdge(3, 5);
    insertEdge(4, 5);
    insertEdge(4, 6);
    Console.Write("Minimum size of a vertex cover = "
        + findMinCover(V, E) +"\n");
  
}
}
  
// This code is contributed by Rajput-Ji



输出:
Minimum size of a vertex cover = 2
Minimum size of a vertex cover = 3

结论:
时间复杂度: O (E * ( V C V/2 + V C V/4 + V C V/8 +…upto V C k ) )

在最坏的情况下,这些项不超过 log(V)。

注意: Gosper 的 hack 仅适用于 V = 31,如果我们采用 'long long int' 而不是 'int' 它可以工作至 V = 63。