2132. Stamping the Grid
Problem Description
The problem presents a grid consisting of cells marked either 0 (empty) or 1 (occupied). The challenge is to cover all the empty cells with stamps of a pre-defined size (stampHeight x stampWidth
) without overlapping any occupied cells. Stamps can overlap each other, they cannot be rotated, and they must fit entirely within the grid boundaries.
Intuition
To solve this problem, we first need to quickly determine whether a stamp can be placed onto a certain position on the grid. The key is to check that all cells under the stamp are empty. To do this efficiently, we employ a "prefix sum matrix." A prefix sum allows us to calculate the sum of any sub-matrix in constant time.
The prefix sum is constructed such that each cell in this auxiliary matrix contains the sum of all cells above and to the left, including the current cell in the original grid. With this prefix sum matrix, we can quickly determine the sum of any sub-matrix by subtracting the appropriate prefix sums.
Now, when placing a stamp on the grid, we mark the cells in the difference matrix that corresponds to the stamp's area. A difference matrix allows us to record changes to a range within the matrix in constant time. By incrementing the top-left corner of the stamp area and decrementing the point just outside the bottom-right corner of the proposed stamp area, we define a range that can receive a stamp.
Next, we build another prefix sum from the difference matrix. This new matrix helps us understand how many stamps cover each cell. As we iterate through the entire grid, we check each empty cell to see if it has been covered by at least one stamp. If we find any empty cell not covered by a stamp, we know the task is impossible, and we return false
.
If all empty cells are covered, we've met the requirements and can return true
. Each step of the solution builds on a logical progression from determining single-cell coverage to ensuring full-grid compliance with the stamp placement rules.
Learn more about Greedy and Prefix Sum patterns.
Solution Approach
The provided solution can be broken down into the following steps, utilizing concepts such as prefix sums and difference matrices:
-
Constructing the Prefix Sum Matrix (
s
): The prefix sum matrix is built so that each cell represents the sum of all cells above and to the left in the grid, including the cell itself. This is calculated bys[i + 1][j + 1] = s[i + 1][j] + s[i][j + 1] - s[i][j] + grid[i][j]
. This step is the foundation for efficiently checking whether a stamp can be placed in a certain area. -
Stamp Placement Check: For a given cell
(i, j)
that we are trying to cover with a stamp, we check whether the sub-matrix defined by the bottom-right corner(x, y)
— wherex
equalsi + stampHeight
andy
equalsj + stampWidth
— is within the bounds and contains only zeroes. This is done by verifyings[x][y] - s[x][j] - s[i][y] + s[i][j] == 0
. -
Updating the Difference Matrix (
d
): If the stamp can be placed, we update the difference matrix to reflect this. We incrementd[i][j]
and decrementd[i][y]
,d[x][j]
, andd[x][y]
to ensure that the affected range captures the stamp placement. -
Applying the Difference Matrix: Another two-dimensional prefix sum is calculated from the difference matrix. For each cell
(i, j)
,cnt[i + 1][j + 1]
is updated to the sum of the current cell plus the cells above and to the left. This represents how many stamps cover each specific cell. -
Validation Check: As we iterate through each cell in the grid again, we verify if it's empty
grid[i][j] == 0
. If it's uncovered by any stampcnt[i + 1][j + 1] == 0
, we immediately returnfalse
as the requirement is violated. -
Returning the Result: If all empty cells are covered without breaking any of the rules (all visited cells pass the validation check), we conclude that it's possible to stamp all empty cells and return
true
.
By using a prefix sum to enable quick sum calculations of sub-matrices and a difference matrix to handle the range updates, the solution efficiently determines whether it's possible to satisfy the stamp placement conditions for the entire grid.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach using a grid of size 3x4
with a stamp size of 2x2
.
Consider the grid:
0 1 0 0 1 0 0 0 0 0 0 0
Let's go through the solution steps on this grid:
-
Construct Prefix Sum Matrix (
s
): We start by creating a prefix sum matrixs
of size4x5
(one more in each dimension for easier calculation).To build prefix sum matrix `s`, we follow the rule: s[i + 1][j + 1] = s[i + 1][j] + s[i][j + 1] - s[i][j] + grid[i][j]. Starting with `s` all zeros: 0 0 0 0 0 0 0 0 We add each cell of the grid, cumulatively: 0 0 0 0 0 0 0 1 1 1 0 1 1 2 2 0 1 2 3 3
-
Stamp Placement Check: Let's check if we can place the
2x2
stamp at the top-left corner(0,0)
.We check the sub-matrix given by
s[2][2]
is zero:s[2][2] - s[0][2] - s[2][0] + s[0][0] = 1 - 0 - 1 + 0 = 0
Since it's not zero, we cannot place the stamp here because there's an occupied cell
(1,0)
within the range of the stamp. -
Updating the Difference Matrix (
d
): Now, let's attempt to place a stamp at(2,0)
. We can confirm it fits by checking the respective prefix sum sub-matrix. Since it fits, we update the difference matrixd
:d starts as all zeros: 0 0 0 0 0 0 0 0 We increment `d[2][0]` and decrement the cells just outside the bottom-right of stamp area `d[4][2]`: 0 0 0 0 0 0 0 1 0 -1
-
Applying the Difference Matrix: Next, we construct a new prefix sum from the
d
:We calculate using d's values: 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 1 1 1 0
This matrix now indicates the number of stamps covering each cell.
-
Validation Check: We go back to our original grid and check each cell:
grid[0][0] == 0
andcnt[1][1] == 0
(no stamp covers this cell), so we returnfalse
.
-
Returning the Result: There is no need to proceed because we already determined that it's impossible to cover all empty cells with stamps, as per the previous step's validation check.
Using these steps serves to highlight how the algorithm leverages the prefix sum and difference matrix to efficiently solve the problem. In this example, the given grid configuration and stamp size do not allow us to cover all empty cells without overlapping an occupied cell. Therefore, the final output would be false
.
Solution Implementation
1class Solution:
2 def possibleToStamp(self, grid: List[List[int]], stampHeight: int, stampWidth: int) -> bool:
3 # Get the dimensions of the grid
4 rows, cols = len(grid), len(grid[0])
5
6 # Initialize prefix sum matrix
7 prefix_sum = [[0] * (cols + 1) for _ in range(rows + 1)]
8 # Calculate the prefix sum of the grid
9 for i in range(rows):
10 for j in range(cols):
11 prefix_sum[i + 1][j + 1] = (
12 prefix_sum[i][j + 1] + prefix_sum[i + 1][j]
13 - prefix_sum[i][j] + grid[i][j]
14 )
15
16 # Initialize difference matrix for stamp placements
17 diff_matrix = [[0] * (cols + 1) for _ in range(rows + 1)]
18 # Determine if a stamp can be placed on an empty area
19 for i in range(rows):
20 for j in range(cols):
21 if grid[i][j] == 0:
22 row_end, col_end = i + stampHeight, j + stampWidth
23 # Check if the stamp fits in the current position
24 if row_end <= rows and col_end <= cols and prefix_sum[row_end][col_end] - prefix_sum[row_end][j] - prefix_sum[i][col_end] + prefix_sum[i][j] == 0:
25 diff_matrix[i][j] += 1
26 diff_matrix[i][col_end] -= 1
27 diff_matrix[row_end][j] -= 1
28 diff_matrix[row_end][col_end] += 1
29
30 # Create matrix to keep track of covered cells
31 coverage_matrix = [[0] * (cols + 1) for _ in range(rows + 1)]
32 # Apply the difference matrix to calculate the number of stamps covering each cell
33 for i in range(rows):
34 for j in range(cols):
35 coverage_matrix[i + 1][j + 1] = (
36 coverage_matrix[i + 1][j] + coverage_matrix[i][j + 1]
37 - coverage_matrix[i][j] + diff_matrix[i][j]
38 )
39 # If a cell is supposed to be stamped but has no stamp coverage, return False
40 if grid[i][j] == 0 and coverage_matrix[i + 1][j + 1] == 0:
41 return False
42 # If every empty cell is covered appropriately with stamps, return True
43 return True
44
1class Solution {
2 public boolean possibleToStamp(int[][] grid, int stampHeight, int stampWidth) {
3 int numRows = grid.length, numCols = grid[0].length;
4 // Prefix sum array to quickly calculate sum of submatrices.
5 int[][] prefixSum = new int[numRows + 1][numCols + 1];
6
7 // Build the prefixSum array.
8 for (int row = 0; row < numRows; ++row) {
9 for (int col = 0; col < numCols; ++col) {
10 prefixSum[row + 1][col + 1] = prefixSum[row + 1][col] + prefixSum[row][col + 1] - prefixSum[row][col] + grid[row][col];
11 }
12 }
13
14 // Difference array to apply range updates (stamping).
15 int[][] diff = new int[numRows + 1][numCols + 1];
16
17 // Iterate over the entire grid to check where stamps can be placed.
18 for (int row = 0; row < numRows; ++row) {
19 for (int col = 0; col < numCols; ++col) {
20 // If we have an empty cell and can fit a stamp starting at (row, col),
21 // then update the difference array.
22 if (grid[row][col] == 0) {
23 int endRow = row + stampHeight, endCol = col + stampWidth;
24 if (endRow <= numRows && endCol <= numCols &&
25 prefixSum[endRow][endCol] - prefixSum[endRow][col] - prefixSum[row][endCol] + prefixSum[row][col] == 0) {
26 diff[row][col]++;
27 diff[row][endCol]--;
28 diff[endRow][col]--;
29 diff[endRow][endCol]++;
30 }
31 }
32 }
33 }
34
35 // Use a running sum to apply the difference array updates to the grid.
36 int[][] coverCount = new int[numRows + 1][numCols + 1];
37 for (int row = 0; row < numRows; ++row) {
38 for (int col = 0; col < numCols; ++col) {
39 coverCount[row + 1][col + 1] = coverCount[row + 1][col] + coverCount[row][col + 1] - coverCount[row][col] + diff[row][col];
40 // If there is an empty cell that is not covered by a stamp, return false.
41 if (grid[row][col] == 0 && coverCount[row + 1][col + 1] == 0) {
42 return false;
43 }
44 }
45 }
46
47 // All empty cells are covered by stamps; return true.
48 return true;
49 }
50}
51
1class Solution {
2public:
3 bool possibleToStamp(vector<vector<int>>& grid, int stampHeight, int stampWidth) {
4 int rows = grid.size(), cols = grid[0].size();
5 // Use an auxiliary matrix to perform prefix sum computations
6 vector<vector<int>> prefixSum(rows + 1, vector<int>(cols + 1));
7 // Calculate the prefix sums for all cells
8 for (int i = 0; i < rows; ++i) {
9 for (int j = 0; j < cols; ++j) {
10 prefixSum[i + 1][j + 1] = prefixSum[i + 1][j] + prefixSum[i][j + 1] - prefixSum[i][j] + grid[i][j];
11 }
12 }
13
14 // Initialize a difference matrix to mark stampable regions
15 vector<vector<int>> diff(rows + 1, vector<int>(cols + 1));
16 for (int i = 0; i < rows; ++i) {
17 for (int j = 0; j < cols; ++j) {
18 // If the current cell is filled, it cannot be stamped, skip it
19 if (grid[i][j]) continue;
20 int x = i + stampHeight, y = j + stampWidth;
21 // Check if it's possible to stamp the area starting at (i, j)
22 if (x <= rows && y <= cols && prefixSum[x][y] - prefixSum[i][y] - prefixSum[x][j] + prefixSum[i][j] == 0) {
23 // Mark corners of the stamp region in the difference matrix
24 diff[i][j]++;
25 diff[x][j]--;
26 diff[i][y]--;
27 diff[x][y]++;
28 }
29 }
30 }
31
32 // Initialize a matrix to hold the stamp count for each cell
33 vector<vector<int>> stampCount(rows + 1, vector<int>(cols + 1));
34 for (int i = 0; i < rows; ++i) {
35 for (int j = 0; j < cols; ++j) {
36 // Calculate the cumulative stamp count for the current cell
37 stampCount[i + 1][j + 1] = stampCount[i + 1][j] + stampCount[i][j + 1] - stampCount[i][j] + diff[i][j];
38 // If the current cell is empty and has no stamps, return false
39 if (grid[i][j] == 0 && stampCount[i + 1][j + 1] == 0) return false;
40 }
41 }
42
43 // If all constraints are satisfied, it's possible to stamp
44 return true;
45 }
46};
47
1// Function to determine if it's possible to stamp the entire grid
2// with a stamp of given height and width.
3/**
4 * @param grid - 2D grid representing the areas to be stamped (0) or not (1)
5 * @param stampHeight - Height of the stamp
6 * @param stampWidth - Width of the stamp
7 * @returns boolean indicating whether it's possible to stamp the whole grid
8 */
9function possibleToStamp(grid: number[][], stampHeight: number, stampWidth: number): boolean {
10 const m: number = grid.length;
11 const n: number = grid[0].length;
12
13 // Initialize prefixes sums arrays with zeros
14 let prefixSums: number[][] = new Array(m + 1).fill(0).map(() => new Array(n + 1).fill(0));
15 let diff: number[][] = new Array(m + 1).fill(0).map(() => new Array(n + 1).fill(0));
16 let count: number[][] = new Array(m + 1).fill(0).map(() => new Array(n + 1).fill(0));
17
18 // Compute the prefix sums of the grid
19 for (let i = 0; i < m; ++i) {
20 for (let j = 0; j < n; ++j) {
21 prefixSums[i + 1][j + 1] = prefixSums[i + 1][j] + prefixSums[i][j + 1] - prefixSums[i][j] + grid[i][j];
22 }
23 }
24
25 // Determine where stamping is possible and mark in the diff array
26 for (let i = 0; i < m; ++i) {
27 for (let j = 0; j < n; ++j) {
28 if (grid[i][j] == 0) {
29 let x: number = i + stampHeight;
30 let y: number = j + stampWidth;
31 if (x <= m && y <= n && prefixSums[x][y] - prefixSums[i][y] - prefixSums[x][j] + prefixSums[i][j] == 0) {
32 diff[i][j]++;
33 diff[i][y]--;
34 diff[x][j]--;
35 diff[x][y]++;
36 }
37 }
38 }
39 }
40
41 // Calculate the influence of stamping using prefix sums of the diff array
42 for (let i = 0; i < m; ++i) {
43 for (let j = 0; j < n; ++j) {
44 count[i + 1][j + 1] = count[i + 1][j] + count[i][j + 1] - count[i][j] + diff[i][j];
45 if (grid[i][j] == 0 && count[i + 1][j + 1] == 0) {
46 return false; // Unstamped cell found, stamping not possible
47 }
48 }
49 }
50
51 return true; // All cells can be stamped
52}
53
54// Example usage
55// let result: boolean = possibleToStamp([[1, 0, 0, 0], [1, 0, 0, 0], [1, 1, 1, 0]], 2, 2);
56// console.log(result); // Should return true or false based on stampability of grid
57
Time and Space Complexity
Time Complexity
The time complexity of the code is determined by several nested loops that iterate over the entire grid
and the use of prefix sums.
-
The first double loop (calculating
s[i + 1][j + 1]
) iterates through allm * n
cells of the grid once, thus it has a complexity ofO(m * n)
. -
The second double loop (calculating
d[i][j]
and checking conditions) also iterates through allm * n
cells of the grid once. Inside this loop, it performs constant-time operations and a check that involves accessing the precomputed prefix sum arrays
. Again, the complexity isO(m * n)
. -
The last double loop (calculating
cnt[i + 1][j + 1]
and verifying that there are no zeros uncovered by stamps) iterates through allm * n
cells of the grid. Since it only performs constant-time operations, the complexity isO(m * n)
.
Since these loops are sequential and not nested within each other (other than the initialization loops for prefix sums which also have the same complexity), the overall time complexity is the sum of the individual complexities, which remains O(m * n)
because the number of iterations is proportional to the size of the grid.
Space Complexity
The space complexity is determined by additional arrays s
, d
, and cnt
which are used for computing prefix sums and keeping track of the stamps.
-
The array
s
has dimensions(m + 1) x (n + 1)
which adds up to a space complexity ofO((m + 1) * (n + 1))
. However, when considering Big O notation, constant factors are dropped, so the complexity isO(m * n)
. -
Similarly, the arrays
d
andcnt
also have dimensions(m + 1) x (n + 1)
, contributing an additionalO(m * n)
space complexity each.
The space needed for the input array grid
is not considered in the space complexity analysis since it is the input to the function, not additional space allocated by the function itself.
In total, since all three arrays are maintained independently, the overall space complexity is O(3 * m * n)
, which simplifies to O(m * n)
under Big O notation since constant factors are ignored.
Therefore, the final space complexity is O(m * n)
.
Learn more about how to find time and space complexity quickly using problem constraints.
What is the best way of checking if an element exists in an unsorted array once in terms of time complexity? Select the best that applies.
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we
Want a Structured Path to Master System Design Too? Don’t Miss This!