Open In App

Maximum Coverage Problem

Last Updated : 03 Oct, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Maximum Coverage Problem (MCP) is a well-known optimization problem in combinatorial optimization and computer science.

 It can be formally defined as follows:

  • A finite set U containing n elements (the universe): U = {e1, e2, …, en}.
  • A collection S of m subsets of U: S = {S1, S2, …, Sm}, where each Si ⊆ U.
  • An integer k, where 1 ≤ k ≤ m.

Objective:

Find a subcollection C ⊆ S consisting of exactly k subsets, such that the total number of elements covered by these subsets is maximized.

The Maximum Coverage Problem is indeed NP-hard, as it can be easily reduced to the Set Cover Problem, which is known to be NP-complete. The optimization version of Set Cover asks for the minimum number of sets to cover all elements in the universe, while the MCP asks for the maximum number of elements that can be covered with a given number of sets.

A greedy algorithm can be used to approximate the solution to the Maximum Coverage Problem, achieving a factor of (1 – 1/e) times the optimal solution, where e is the base of the natural logarithm. The algorithm works as follows:

  • Initialize an empty subcollection C = {}.
  • For i = 1 to k:
    • Choose the set Si ∈ S that maximizes the number of new elements covered (i.e., not already covered by sets in C).
    • Add Si to the subcollection C.
    • Remove Si from collection S.

The greedy algorithm is simple and has a proven approximation guarantee, but it is not guaranteed to find the optimal solution. Other approaches, such as integer linear programming, can be used to find the optimal solution, but they are typically much slower than the greedy algorithm, especially for large instances of the problem.

Examples:

n = 5, m =  4, k =  2

Subsets
2 1 2
2 2 3
2 3 4
2 4 5

This input specifies the following information:

  • The universe contains 5 elements (numbered from 1 to 5).
  • There are 4 subsets available.
  • We need to choose exactly 2 subsets to maximize the coverage.

The subsets are defined as follows:

  • Subset 0: {1, 2}
  • Subset 1: {2, 3}
  • Subset 2: {3, 4}
  • Subset 3: {4, 5}

Output:

Chosen subsets: 0 2 
Number of elements covered: 4

Explanation:

  • The greedy algorithm starts with an empty set of covered elements and iterates k = 2 times.
  • In the first iteration, the algorithm checks which subset has the maximum number of new elements to cover. At this point, all subsets have their elements not covered yet. The algorithm selects the subset with the highest number of elements, which is subset 0 ({1, 2}). The set of covered elements now becomes {1, 2}.
  • In the second iteration, the algorithm again checks which remaining subset has the maximum number of new elements to cover. The remaining subsets are subsets 1, 2, and 3. The new elements in each subset are:
  • Subset 1: {2, 3} (1 new element, as 2 is already covered)
  • Subset 2: {3, 4} (1 new element, as 3 is already covered)
  • Subset 3: {4, 5} (2 new elements)
  • The algorithm selects subset 2 ({3, 4}) since it has the highest number of new elements (1). The set of covered elements now becomes {1, 2, 3, 4}.
  • The algorithm has completed k iterations, and we have chosen subsets 0 and 2. These two subsets cover a total of 4 elements.
  • The output displays the chosen subsets as “0 2” and the number of covered elements as “4”.

Implementation of the greedy algorithm for the Maximum Coverage Problem:

C++




// C++ code for the above approach:
#include <iostream>
#include <set>
#include <vector>
 
// Function to find the set with the
// maximum number of new elements
int find_max_new_elements(
    const std::vector<std::set<int> >& subsets,
    const std::set<int>& covered)
{
    int max_new_elements = 0;
    int max_subset_index = -1;
 
    for (int i = 0; i < subsets.size(); ++i) {
        int new_elements = 0;
        for (int element : subsets[i]) {
            if (covered.find(element) == covered.end()) {
                ++new_elements;
            }
        }
 
        if (new_elements > max_new_elements) {
            max_new_elements = new_elements;
            max_subset_index = i;
        }
    }
 
    return max_subset_index;
}
 
// Drivers code
int main()
{
    int n, m, k;
 
    // Example input:
 
    // Number of elements in
    // the universe
    n = 5;
 
    // Number of subsets
    m = 4;
 
    // Number of subsets to choose
    k = 2;
 
    std::vector<std::set<int> > subsets
        = { { 1, 2 }, { 2, 3 }, { 3, 4 }, { 4, 5 } };
 
    std::set<int> covered;
    std::vector<int> solution;
 
    // Greedy algorithm for Maximum
    // Coverage Problem
    for (int i = 0; i < k; ++i) {
        int max_subset_index
            = find_max_new_elements(subsets, covered);
 
        if (max_subset_index != -1) {
            solution.push_back(max_subset_index);
            covered.insert(
                subsets[max_subset_index].begin(),
                subsets[max_subset_index].end());
        }
        else {
            break;
        }
    }
 
    // Print the solution (indices of the
    // chosen subsets) and the number
    // of covered elements
    std::cout << "Chosen subsets: ";
    for (int index : solution) {
        std::cout << index << " ";
    }
    std::cout << std::endl
              << "Number of elements covered: "
              << covered.size() << std::endl;
 
    return 0;
}


Java




// java code for above approach
import java.util.*;
 
class GFG {
    // Function to find the set with the
    // maximum number of new elements
    static int
    findMaxNewElements(List<Set<Integer> > subsets,
                       Set<Integer> covered)
    {
        int maxNewElements = 0;
        int maxSubsetIndex = -1;
 
        for (int i = 0; i < subsets.size(); i++) {
            int newElements = 0;
            for (int element : subsets.get(i)) {
                if (!covered.contains(element)) {
                    newElements++;
                }
            }
 
            if (newElements > maxNewElements) {
                maxNewElements = newElements;
                maxSubsetIndex = i;
            }
        }
 
        return maxSubsetIndex;
    }
 
    // Driver code
    public static void main(String[] args)
    {
        int n, m, k;
 
        // Example input:
 
        // Number of elements in the universe
        n = 5;
 
        // Number of subsets
        m = 4;
 
        // Number of subsets to choose
        k = 2;
 
        List<Set<Integer> > subsets = new ArrayList<>();
        subsets.add(new HashSet<>(Arrays.asList(1, 2)));
        subsets.add(new HashSet<>(Arrays.asList(2, 3)));
        subsets.add(new HashSet<>(Arrays.asList(3, 4)));
        subsets.add(new HashSet<>(Arrays.asList(4, 5)));
 
        Set<Integer> covered = new HashSet<>();
        List<Integer> solution = new ArrayList<>();
 
        // Greedy algorithm for Maximum Coverage Problem
        for (int i = 0; i < k; i++) {
            int maxSubsetIndex
                = findMaxNewElements(subsets, covered);
 
            if (maxSubsetIndex != -1) {
                solution.add(maxSubsetIndex);
                covered.addAll(subsets.get(maxSubsetIndex));
            }
            else {
                break;
            }
        }
 
        // Print the solution (indices of the chosen
        // subsets) and the number of covered elements
        System.out.print("Chosen subsets: ");
        for (int index : solution) {
            System.out.print(index + " ");
        }
        System.out.println("\nNumber of elements covered: "
                           + covered.size());
    }
}


Python3




# python code for the above approach:
 
def find_max_new_elements(subsets, covered):
    max_new_elements = 0
    max_subset_index = -1
 
    for i in range(len(subsets)):
        new_elements = 0
        for element in subsets[i]:
            if element not in covered:
                new_elements += 1
 
        if new_elements > max_new_elements:
            max_new_elements = new_elements
            max_subset_index = i
 
    return max_subset_index
 
 
# Driver code
n, m, k = 5, 4, 2
 
subsets = [{1, 2}, {2, 3}, {3, 4}, {4, 5}]
 
covered = set()
solution = []
 
# Greedy algorithm for Maximum Coverage Problem
for i in range(k):
    max_subset_index = find_max_new_elements(subsets, covered)
 
    if max_subset_index != -1:
        solution.append(max_subset_index)
        covered.update(subsets[max_subset_index])
    else:
        break
 
# Print the solution (indices of the chosen subsets) and the number of covered elements
print("Chosen subsets:", end=" ")
for index in solution:
    print(index, end=" ")
print()
print("Number of elements covered:", len(covered))


C#




// C# code for the above approach
using System;
using System.Collections.Generic;
 
namespace MaximumCoverageProblem {
public class GFG {
    // Function to find the set with the
    // maximum number of new elements
    static int
    FindMaxNewElements(List<HashSet<int> > subsets,
                       HashSet<int> covered, int m)
    {
        int maxNewElements = 0;
        int maxSubsetIndex = -1;
 
        for (int i = 0; i < m; ++i) {
            int newElements = 0;
            foreach(int element in subsets[i])
            {
                if (!covered.Contains(element)) {
                    ++newElements;
                }
            }
 
            if (newElements > maxNewElements) {
                maxNewElements = newElements;
                maxSubsetIndex = i;
            }
        }
 
        return maxSubsetIndex;
    }
 
    // Driver code
    static void Main(string[] args)
    {
        int m, k;
 
        // Example input:
 
        // Number of subsets
        m = 4;
 
        // Number of subsets to choose
        k = 2;
 
        List<HashSet<int> > subsets
            = new List<HashSet<int> >{
                  new HashSet<int>{ 1, 2 },
                  new HashSet<int>{ 2, 3 },
                  new HashSet<int>{ 3, 4 },
                  new HashSet<int>{ 4, 5 }
              };
 
        HashSet<int> covered = new HashSet<int>();
        List<int> solution = new List<int>();
 
        // Greedy algorithm for Maximum
        // Coverage Problem
        for (int i = 0; i < k; ++i) {
            int maxSubsetIndex
                = FindMaxNewElements(subsets, covered, m);
 
            if (maxSubsetIndex != -1) {
                solution.Add(maxSubsetIndex);
                covered.UnionWith(subsets[maxSubsetIndex]);
            }
            else {
                break;
            }
        }
 
        // Print the solution (indices of the
        // chosen subsets) and the number
        // of covered elements
        Console.Write("Chosen subsets: ");
        foreach(int index in solution)
        {
            Console.Write(index + " ");
        }
        Console.WriteLine("\nNumber of elements covered: "
                          + covered.Count);
    }
}
}
 
// This code is contributed by Susobhan Akhuli


Javascript




function findMaxNewElements(subsets, covered) {
  let maxNewElements = 0;
  let maxSubsetIndex = -1;
 
  for (let i = 0; i < subsets.length; i++) {
    let newElements = 0;
    for (let element of subsets[i]) {
      if (!covered.has(element)) {
        newElements++;
      }
    }
 
    if (newElements > maxNewElements) {
      maxNewElements = newElements;
      maxSubsetIndex = i;
    }
  }
 
  return maxSubsetIndex;
}
 
function maximumCoverage(n, m, k, subsets) {
  let covered = new Set();
  let solution = [];
 
  for (let i = 0; i < k; i++) {
    const maxSubsetIndex = findMaxNewElements(subsets, covered);
 
    if (maxSubsetIndex !== -1) {
      solution.push(maxSubsetIndex);
      subsets[maxSubsetIndex].forEach(element => covered.add(element));
    } else {
      break;
    }
  }
 
  return { solution, numCovered: covered.size };
}
 
// Example input:
const n = 5;
const m = 4;
const k = 2;
const subsets = [
  new Set([1, 2]),
  new Set([2, 3]),
  new Set([3, 4]),
  new Set([4, 5]),
];
 
const result = maximumCoverage(n, m, k, subsets);
 
console.log("Chosen subsets:", result.solution);
console.log("Number of elements covered:", result.numCovered);


Output

Chosen subsets: 0 2 
Number of elements covered: 4

Time Complexity: O(k * m * n)
Auxiliary Space: O(m * n)



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads