Facebook Pixel

3111. Minimum Rectangles to Cover Points


Problem Description

You are given a 2D integer array points, where points[i] = [x_i, y_i]. You are also given an integer w. Your task is to cover all the given points with rectangles.

Each rectangle has its lower end at some point (x_1, 0) and its upper end at some point (x_2, y_2), where x_1 <= x_2, y_2 >= 0, and the condition x_2 - x_1 <= w must be satisfied for each rectangle.

A point is considered covered by a rectangle if it lies within or on the boundary of the rectangle.

Return an integer denoting the minimum number of rectangles needed so that each point is covered by at least one rectangle.

Note: A point may be covered by more than one rectangle.

Intuition

To solve this problem, the key observation is that the height of the rectangles does not affect the coverage of points since we are only given a width w. This allows us to focus solely on the x-coordinates of the points.

  1. Sort the Points: Since our goal is to cover all points with the minimum number of rectangles, sorting the points by their x-coordinates helps us efficiently check which points can be covered together.

  2. Greedy Approach: We will use a greedy approach to cover the points. After sorting, we will traverse the list from left to right. At each point, we'll use a variable x1 to keep track of the rightmost x-coordinate that the current rectangle can cover (x1 = -1 initially).

  3. Check and Cover: As we iterate through the points, if a point's x-coordinate is greater than x1, it means the existing rectangle cannot cover it. Thus, we must place a new rectangle, increasing our count by one, and update x1 to the coordinate x + w of the current point's x-coordinate.

Through this approach, we can efficiently determine the minimum number of rectangles needed by ensuring we always extend the coverage as far right as possible, which is optimized by starting at the leftmost point and greedily covering additional points with each rectangle.

Learn more about Greedy and Sorting patterns.

Solution Approach

The solution uses a greedy strategy combined with sorting to efficiently determine the minimum number of rectangles required to cover all given points. Here is a detailed breakdown of the approach:

  1. Sorting:

    • The first step is to sort the list of points based on their x-coordinates. Sorting helps in efficiently managing which points can be grouped together under the same rectangle.
    • This is performed using the sort() function: points.sort().
  2. Initialization:

    • Initialize a counter ans to track the number of rectangles used.
    • Use x1 = -1 to keep track of the furthest x-coordinate a rectangle can currently cover.
  3. Iterative Greedy Process:

    • Iterate over each point (x, _) in the sorted list:
      • If the current point's x-coordinate x exceeds x1, it indicates that the current rectangle can no longer cover this point.
      • In this situation, increase the rectangle count ans by 1, and update x1 to x + w to represent the furthest point that the new rectangle can cover.
  4. Final Output:

    • After processing all points, ans will contain the minimum number of rectangles needed to cover all points.

The algorithm runs in O(n log n) time complexity due to the initial sort step, where n is the number of points. The subsequent single pass through the points is O(n), making the overall approach efficient. The data structures used are primarily lists, which handle the input points and sorting operation straightforwardly.

Here is the core part of the solution in code form:

points.sort()
ans, x1 = 0, -1
for x, _ in points:
    if x > x1:
        ans += 1
        x1 = x + w
return ans

Ready to land your dream job?

Unlock your dream job with a 2-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Consider an example where the points are given as points = [[1, 2], [3, 4], [7, 2], [9, 3]], and the width w is 4. We want to determine the minimum number of rectangles needed to cover these points.

  1. Sort the Points:

    • First, we sort the points based on their x-coordinates, resulting in points = [[1, 2], [3, 4], [7, 2], [9, 3]].
  2. Initialization:

    • Initialize ans = 0 to track the number of rectangles used, and x1 = -1 to represent the furthest x-coordinate a rectangle can cover initially.
  3. Iterative Greedy Process:

    • Start iterating over the sorted points:

      • First Point (1, 2):

        • The x-coordinate 1 is greater than x1 (-1), which means we need a new rectangle.
        • Increment ans by 1 (ans = 1).
        • Update x1 to 1 + 4 = 5, indicating the furthest this rectangle can cover.
      • Second Point (3, 4):

        • The x-coordinate 3 is less than x1 (5), so it's covered by the current rectangle. No rectangle is added.
      • Third Point (7, 2):

        • The x-coordinate 7 is greater than x1 (5), requiring a new rectangle.
        • Increment ans by 1 (ans = 2).
        • Update x1 to 7 + 4 = 11.
      • Fourth Point (9, 3):

        • The x-coordinate 9 is less than x1 (11), thus it is covered by the current rectangle. No rectangle is added.
  4. Final Output:

    • After processing all points, the minimum number of rectangles needed is ans = 2.

Thus, the method accurately calculates that two rectangles are necessary to cover all the given points with the specified conditions.

Solution Implementation

1from typing import List
2
3class Solution:
4    def minRectanglesToCoverPoints(self, points: List[List[int]], w: int) -> int:
5        # Sort the list of points based on the x-coordinate
6        points.sort()
7
8        # Initialize the number of rectangles required and the rightmost edge of the last placed rectangle
9        ans = 0
10        x1 = -1  # -1 ensures the first point is always covered
11
12        # Iterate through each point in the sorted list
13        for x, _ in points:
14            # If the current x-coordinate is outside the coverage of the last rectangle
15            if x > x1:
16                ans += 1  # Increment the number of rectangles used
17                x1 = x + w  # Update the rightmost edge of the newly placed rectangle
18
19        # Return the total number of rectangles required
20        return ans
21
1import java.util.Arrays;
2
3class Solution {
4    /**
5     * This method calculates the minimum number of rectangles required
6     * to cover all given points on a 2D plane, given a fixed rectangle width `w`.
7     * 
8     * @param points An array of integer arrays, where each inner array represents the coordinates [x, y] of a point.
9     * @param w The width of each rectangle.
10     * @return The minimum number of rectangles needed to cover all the points.
11     */
12    public int minRectanglesToCoverPoints(int[][] points, int w) {
13        // Sort the points based on their x-coordinates
14        Arrays.sort(points, (a, b) -> a[0] - b[0]);
15
16        int rectangleCount = 0; // To keep track of the number of rectangles used
17        int currentXLimit = -1; // The rightmost x-coordinate that has been covered so far
18
19        // Iterate over each point
20        for (int[] point : points) {
21            int xCoordinate = point[0]; // Extract the x-coordinate of the current point
22
23            // If the current x-coordinate is greater than the rightmost covered limit,
24            // a new rectangle is needed
25            if (xCoordinate > currentXLimit) {
26                rectangleCount++; // Increment the rectangle count
27                currentXLimit = xCoordinate + w; // Update the covered limit
28            }
29        }
30
31        return rectangleCount; // Return the total number of rectangles needed
32    }
33}
34
1#include <vector>
2#include <algorithm>
3
4class Solution {
5public:
6    // Method to calculate the minimum number of rectangles needed to cover all points
7    int minRectanglesToCoverPoints(std::vector<std::vector<int>>& points, int width) {
8        // Sort points based on their x-coordinates
9        std::sort(points.begin(), points.end());
10      
11        // Initialize the number of rectangles to zero
12        int rectanglesCount = 0;
13        // Initialize the rightmost x-coordinate already covered by rectangles to -1
14        int rightmostCoveredX = -1;
15      
16        // Iterate over each point
17        for (const auto& point : points) {
18            int xCoordinate = point[0];
19            // If the current point is not covered by the last rectangle
20            if (xCoordinate > rightmostCoveredX) {
21                // Increment the rectangle count to cover this point
22                ++rectanglesCount;
23                // Update the rightmost x-coordinate now covered by the rectangle
24                rightmostCoveredX = xCoordinate + width;
25            }
26        }
27      
28        // Return the total number of rectangles used
29        return rectanglesCount;
30    }
31};
32
1// Function to determine the minimum number of rectangles required to cover all points
2function minRectanglesToCoverPoints(points: number[][], w: number): number {
3    // First, sort the points based on the x-coordinate in increasing order
4    points.sort((a, b) => a[0] - b[0]);
5
6    // Initialize the number of rectangles needed and the x-coordinate end limit
7    let ans = 0;
8    let x1 = -1;
9
10    // Iterate over each point
11    for (const [x, _] of points) {
12        // If the current x-coordinate is greater than the current rectangle's end, place a new rectangle
13        if (x > x1) {
14            ans++;       // Increment the rectangle counter
15            x1 = x + w;  // Update the end limit for the current rectangle
16        }
17    }
18
19    // Return the total number of rectangles used
20    return ans;
21}
22

Time and Space Complexity

The time complexity of the code is O(n \log n). This is because the points are sorted at the beginning, which takes O(n \log n) time, where n is the number of points. The subsequent loop runs in O(n) time, iterating through the sorted list of points exactly once, but this does not affect the overall O(n \log n) complexity due to the dominance of the sorting operation.

The space complexity of the code is O(\log n). This arises from the space needed for sorting, where the space complexity is typically O(\log n) due to the recursive nature of sorting algorithms like Timsort, used in Python.

Learn more about how to find time and space complexity quickly.


Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:
Question 1 out of 10

What does the following code do?

1def f(arr1, arr2):
2  i, j = 0, 0
3  new_arr = []
4  while i < len(arr1) and j < len(arr2):
5      if arr1[i] < arr2[j]:
6          new_arr.append(arr1[i])
7          i += 1
8      else:
9          new_arr.append(arr2[j])
10          j += 1
11  new_arr.extend(arr1[i:])
12  new_arr.extend(arr2[j:])
13  return new_arr
14
1public static List<Integer> f(int[] arr1, int[] arr2) {
2  int i = 0, j = 0;
3  List<Integer> newArr = new ArrayList<>();
4
5  while (i < arr1.length && j < arr2.length) {
6      if (arr1[i] < arr2[j]) {
7          newArr.add(arr1[i]);
8          i++;
9      } else {
10          newArr.add(arr2[j]);
11          j++;
12      }
13  }
14
15  while (i < arr1.length) {
16      newArr.add(arr1[i]);
17      i++;
18  }
19
20  while (j < arr2.length) {
21      newArr.add(arr2[j]);
22      j++;
23  }
24
25  return newArr;
26}
27
1function f(arr1, arr2) {
2  let i = 0, j = 0;
3  let newArr = [];
4  
5  while (i < arr1.length && j < arr2.length) {
6      if (arr1[i] < arr2[j]) {
7          newArr.push(arr1[i]);
8          i++;
9      } else {
10          newArr.push(arr2[j]);
11          j++;
12      }
13  }
14  
15  while (i < arr1.length) {
16      newArr.push(arr1[i]);
17      i++;
18  }
19  
20  while (j < arr2.length) {
21      newArr.push(arr2[j]);
22      j++;
23  }
24  
25  return newArr;
26}
27

Recommended Readings

Want a Structured Path to Master System Design Too? Donโ€™t Miss This!


Load More