Minimum Rectangles to Cover Points
MedPrompt
You are given a 2D integer array points, where points[i] = [xi, yi]. 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 (x1, 0) and its upper end at some point (x2, y2), where x1 <= x2, y2 >= 0, and the condition x2 - x1 <= 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.
Example 1:

Input: points = [[2,1],[1,0],[1,4],[1,8],[3,5],[4,6]], w = 1
Output: 2
Explanation:
The image above shows one possible placement of rectangles to cover the points:
- A rectangle with a lower end at
(1, 0)and its upper end at(2, 8) - A rectangle with a lower end at
(3, 0)and its upper end at(4, 8)
Example 2:

Input: points = [[0,0],[1,1],[2,2],[3,3],[4,4],[5,5],[6,6]], w = 2
Output: 3
Explanation:
The image above shows one possible placement of rectangles to cover the points:
- A rectangle with a lower end at
(0, 0)and its upper end at(2, 2) - A rectangle with a lower end at
(3, 0)and its upper end at(5, 5) - A rectangle with a lower end at
(6, 0)and its upper end at(6, 6)
Example 3:

Input: points = [[2,3],[1,2]], w = 0
Output: 2
Explanation:
The image above shows one possible placement of rectangles to cover the points:
- A rectangle with a lower end at
(1, 0)and its upper end at(1, 2) - A rectangle with a lower end at
(2, 0)and its upper end at(2, 3)
Constraints:
1 <= points.length <= 105points[i].length == 20 <= xi == points[i][0] <= 1090 <= yi == points[i][1] <= 1090 <= w <= 109- All pairs
(xi, yi)are distinct.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach uses dynamic programming to find the minimum number of rectangles. After sorting the points by their x-coordinate, it builds a solution for the first i points by leveraging the solutions for smaller subproblems. It systematically checks all possible groupings for the last rectangle.
Algorithm
-
- Sort the
pointsarray by x-coordinate. - 2. Create adparray of sizen, wheredp[i]is the minimum rectangles to cover points0toi. - 3. Iterateifrom0ton-1. - 4. For eachi, iteratejfromidown to0. - 5. Ifpoints[i][0] - points[j][0] <= w, updatedp[i]withmin(dp[i], (j > 0 ? dp[j-1] : 0) + 1). - 6. If the condition fails, break the inner loop as further points won't satisfy it. - 7. The final answer isdp[n-1].
- Sort the
Walkthrough
The problem can be solved using dynamic programming after an initial sorting step. The key observation is that the y-coordinates of the points are irrelevant for determining the minimum number of rectangles; only the x-coordinates matter. The width constraint w is the deciding factor for grouping points. The algorithm proceeds as follows:
- Sort: First, we sort the
pointsarray based on the x-coordinates in non-decreasing order. This allows us to process points from left to right. - DP State: We define a DP array,
dp, wheredp[i]stores the minimum number of rectangles required to cover the firsti+1points (i.e.,points[0]topoints[i]). - DP Transition: To calculate
dp[i], we consider thei-th point. We must place it in a rectangle. This rectangle can potentially cover previous points as well. We iterate backwards from pointiwith an indexj. As long aspoints[i][0] - points[j][0] <= w, all points fromjtoican be covered by a single new rectangle. The total cost would be1(for this new rectangle) plus the cost to cover points up toj-1, which isdp[j-1]. We take the minimum over all validj. The recurrence relation is:dp[i] = min(dp[j-1] + 1)for alljfrom0toisuch thatpoints[i][0] - points[j][0] <= w(withdp[-1]being0). The final answer isdp[n-1]. Here is the Java implementation:
import java.util.Arrays;import java.util.Comparator; class Solution { public int minRectanglesToCoverPoints(int[][] points, int w) { int n = points.length; if (n == 0) { return 0; } Arrays.sort(points, Comparator.comparingInt(p -> p[0])); int[] dp = new int[n]; // dp[i] = min rectangles to cover points 0...i for (int i = 0; i < n; i++) { // Initialize with the case where point i starts a new rectangle // covering only itself, adding to the solution for points 0...i-1. dp[i] = (i > 0 ? dp[i-1] : 0) + 1; // Try to group with previous points for (int j = i; j >= 0; j--) { if (points[i][0] - points[j][0] <= w) { // Points from j to i can be in one rectangle. // The cost is 1 (for this rectangle) + cost to cover points up to j-1. int prevCost = (j > 0) ? dp[j-1] : 0; dp[i] = Math.min(dp[i], prevCost + 1); } else { // Since sorted, no point before j will satisfy the condition break; } } } return dp[n-1]; }}Complexity
Time
O(N^2). The initial sort takes O(N log N). The nested loops for the DP calculation result in a quadratic time complexity, which is the dominant factor.
Space
O(N), where N is the number of points. This is for storing the DP array. Additional space might be required for sorting, typically O(log N) or O(N).
Trade-offs
Pros
Provides a structured way to arrive at the correct solution.
Guaranteed to be correct if implemented properly.
Cons
Inefficient for large inputs due to its O(N^2) time complexity, which will likely time out on larger constraints.
Requires O(N) extra space for the DP table.
A highly efficient greedy approach is optimal for this problem. By sorting the points by their x-coordinate, we can iterate through them once. We greedily form rectangles that are as wide as possible to cover the maximum number of points at each step, which leads to the minimum total number of rectangles.
Algorithm
-
- Sort the
pointsarray based on the x-coordinate. - 2. If the array is empty, return 0. - 3. Initializerectangles = 1andcurrentRectStartX = points[0][0]. - 4. Iterate through the points starting from the second point (i=1). - 5. If the current point's x-coordinatepoints[i][0]is greater thancurrentRectStartX + w, it means the point cannot be covered by the current rectangle. - 6. In that case, incrementrectanglesand start a new rectangle by updatingcurrentRectStartXtopoints[i][0]. - 7. After the loop finishes, return the totalrectanglescount.
- Sort the
Walkthrough
This problem has an optimal greedy substructure, which allows for a much more efficient solution than dynamic programming. The core idea remains the same: the problem is one-dimensional, concerning only the x-coordinates of the points. The greedy strategy is as follows:
- Sort: Sort the points based on their x-coordinates. This is crucial as it allows us to consider points in a left-to-right sweep.
- Greedy Choice: Iterate through the sorted points. When we encounter the first uncovered point, we must place a new rectangle to cover it. To be maximally efficient (i.e., greedy), we should make this single rectangle cover as many subsequent points as possible.
- Extend and Cover: We start a new rectangle whose x-range begins at the current point's x-coordinate, let's call it
x_start. This rectangle can cover any pointpwhose x-coordinatep.xis in the range[x_start, x_start + w]. We use this one rectangle to cover the current point and all subsequent points that fall within this range. - Iterate: After covering a group of points with one rectangle, we move our consideration to the next point that was not covered and repeat the process until all points are accounted for. This greedy choice is optimal because by extending the rectangle to its maximum possible width, we cover the largest possible set of upcoming points. This choice never prevents us from finding an optimal solution for the remaining points. Here is the Java implementation:
import java.util.Arrays;import java.util.Comparator; class Solution { public int minRectanglesToCoverPoints(int[][] points, int w) { if (points.length == 0) { return 0; } // Sort the points based on their x-coordinates Arrays.sort(points, Comparator.comparingInt(p -> p[0])); int rectangles = 1; int currentRectStartX = points[0][0]; for (int i = 1; i < points.length; i++) { // If the current point's x-coordinate is outside the // current rectangle's coverage if (points[i][0] > currentRectStartX + w) { // We need a new rectangle rectangles++; // Start the new rectangle from the current point's x-coordinate currentRectStartX = points[i][0]; } // Otherwise, the current point is covered by the existing rectangle, // so we do nothing and move to the next point. } return rectangles; }}Complexity
Time
O(N log N), where N is the number of points. This is dominated by the initial sorting step. The subsequent greedy scan of the array is a single pass, taking O(N) time.
Space
O(log N) or O(N), depending on the sorting algorithm's implementation. An in-place sort like quicksort uses O(log N) stack space, while a stable sort like Timsort might use O(N) space in the worst case.
Trade-offs
Pros
Very efficient, with a time complexity dominated by the sorting step.
Simple and intuitive to implement.
Space-efficient, requiring minimal extra memory.
Cons
The main dependency is on sorting, which might be a drawback if the input must remain unchanged, thus requiring a copy.
Solutions
Solution
public class Solution { public int MinRectanglesToCoverPoints(int[][] points, int w) { Array.Sort(points, (a, b) => a[0] - b[0]); int ans = 0, x1 = -1; foreach(int[] p in points) { int x = p[0]; if (x > x1) { ans++; x1 = x + w; } } return ans; }}class Solution {public int minRectanglesToCoverPoints(int[][] points, int w) { Arrays.sort(points, (a, b)->a[0] - b[0]); int ans = 0; int x1 = -(1 << 30); for (int[] p : points) { int x = p[0]; if (x1 + w < x) { x1 = x; ++ans; } } return ans; }}class Solution {public: int minRectanglesToCoverPoints(vector<vector<int>> &points, int w) { sort(points.begin(), points.end()); int ans = 0, x1 = -(1 << 30); for (auto &p : points) { int x = p[0]; if (x1 + w < x) { x1 = x; ++ans; } } return ans; }};class Solution: def minRectanglesToCoverPoints(self, points: List[List[int]], w: int) -> int: points . sort() ans, x1 = 0, - inf for x, _ in points: if x1 + w < x: x1 = x ans += 1 return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.