2132-用邮票贴满网格图

Raphael Liu Lv10

给你一个 m x n 的二进制矩阵 grid ,每个格子要么为 0 (空)要么为 1 (被占据)。

给你邮票的尺寸为 stampHeight x stampWidth 。我们想将邮票贴进二进制矩阵中,且满足以下 限制要求

  1. 覆盖所有 格子。
  2. 不覆盖任何 **被占据 **的格子。
  3. 我们可以放入任意数目的邮票。
  4. 邮票可以相互有 重叠 部分。
  5. 邮票不允许 旋转
  6. 邮票必须完全在矩阵

如果在满足上述要求的前提下,可以放入邮票,请返回 true ,否则返回 _ _false

示例 1:

**输入:** grid = [[1,0,0,0],[1,0,0,0],[1,0,0,0],[1,0,0,0],[1,0,0,0]], stampHeight = 4, stampWidth = 3
**输出:** true
**解释:** 我们放入两个有重叠部分的邮票(图中标号为 1 和 2),它们能覆盖所有与空格子。

示例 2:

**输入:** grid = [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]], stampHeight = 2, stampWidth = 2 
**输出:** false 
**解释:** 没办法放入邮票覆盖所有的空格子,且邮票不超出网格图以外。

提示:

  • m == grid.length
  • n == grid[r].length
  • 1 <= m, n <= 105
  • 1 <= m * n <= 2 * 105
  • grid[r][c] 要么是 0 ,要么是 1
  • 1 <= stampHeight, stampWidth <= 105

不了解二维前缀和的同学可以先看看 304. 二维区域和检索 - 矩阵不可变

二维差分可以结合二维前缀和与一维差分的思想推导出来。当我们对一个左上角在 (x1,y1),右下角在 (x2,y2) 矩形区域全部增加 x 时,相当于在二维差分矩阵上对 (x1,y1) 增加 x,对 (x1,y2+1) 和 (x2+1,y1) 减少 x,由于这样两个地方都减少了 x,我们还需要在 (x2+1,y2+1) 处增加 x,读者可以用二维前缀和对比体会这一做法。

回到本题。由于邮票可以互相重叠,我们遵从能放就放邮票的策略,遍历所有的空位,尝试以该空位为左上角放置邮票。如果这一矩形没有出界且区域内没有被占据的格子,那么就可以放置邮票,并按照二维差分的做法将区域内的所有元素值加一。判断区域内有没有被占据的格子,可以先求出 grid 的二维前缀和,这样可以 O(1) 判断。

遍历结束后,我们需要从二维差分矩阵还原出二维计数矩阵,这可以通过对二维差分矩阵求二维前缀和求出。遍历计数矩阵,如果存在一个空格子的计数值为 0,就表明该空格子没有被邮票覆盖,返回 false,否则返回 true。

时间复杂度和空间复杂度均为 O(mn)。

[sol1-Python3]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution:
def possibleToStamp(self, grid: List[List[int]], stampHeight: int, stampWidth: int) -> bool:
m, n = len(grid), len(grid[0])
sum = [[0] * (n + 1) for _ in range(m + 1)]
diff = [[0] * (n + 1) for _ in range(m + 1)]
for i, row in enumerate(grid):
for j, v in enumerate(row): # grid 的二维前缀和
sum[i + 1][j + 1] = sum[i + 1][j] + sum[i][j + 1] - sum[i][j] + v

for i, row in enumerate(grid):
for j, v in enumerate(row):
if v == 0:
x, y = i + stampHeight, j + stampWidth # 注意这是矩形右下角横纵坐标都 +1 后的位置
if x <= m and y <= n and sum[x][y] - sum[x][j] - sum[i][y] + sum[i][j] == 0:
diff[i][j] += 1
diff[i][y] -= 1
diff[x][j] -= 1
diff[x][y] += 1 # 更新二维差分

# 还原二维差分矩阵对应的计数矩阵,这里用滚动数组实现
cnt, pre = [0] * (n + 1), [0] * (n + 1)
for i, row in enumerate(grid):
for j, v in enumerate(row):
cnt[j + 1] = cnt[j] + pre[j + 1] - pre[j] + diff[i][j]
if cnt[j + 1] == 0 and v == 0:
return False
cnt, pre = pre, cnt
return True
[sol1-C++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Solution {
public:
bool possibleToStamp(vector<vector<int>> &grid, int stampHeight, int stampWidth) {
int m = grid.size(), n = grid[0].size();
vector<vector<int>> sum(m + 1, vector<int>(n + 1)), diff(m + 1, vector<int>(n + 1));
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) { // grid 的二维前缀和
sum[i + 1][j + 1] = sum[i + 1][j] + sum[i][j + 1] - sum[i][j] + grid[i][j];
}
}

for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
int x = i + stampHeight, y = j + stampWidth; // 注意这是矩形右下角横纵坐标都 +1 后的位置
if (x <= m && y <= n && sum[x][y] - sum[x][j] - sum[i][y] + sum[i][j] == 0) {
++diff[i][j];
--diff[i][y];
--diff[x][j];
++diff[x][y]; // 更新二维差分
}
}
}

// 还原二维差分矩阵对应的计数矩阵,这里用滚动数组实现
vector<int> cnt(n + 1), pre(n + 1);
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
cnt[j + 1] = cnt[j] + pre[j + 1] - pre[j] + diff[i][j];
if (cnt[j + 1] == 0 && grid[i][j] == 0) {
return false;
}
}
swap(cnt, pre);
}
return true;
}
};
[sol1-Go]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
func possibleToStamp(grid [][]int, stampHeight, stampWidth int) bool {
m, n := len(grid), len(grid[0])
sum := make([][]int, m+1)
sum[0] = make([]int, n+1)
diff := make([][]int, m+1)
diff[0] = make([]int, n+1)
for i, row := range grid {
sum[i+1] = make([]int, n+1)
for j, v := range row { // grid 的二维前缀和
sum[i+1][j+1] = sum[i+1][j] + sum[i][j+1] - sum[i][j] + v
}
diff[i+1] = make([]int, n+1)
}

for i, row := range grid {
for j, v := range row {
if v == 0 {
x, y := i+stampHeight, j+stampWidth // 注意这是矩形右下角横纵坐标都 +1 后的位置
if x <= m && y <= n && sum[x][y]-sum[x][j]-sum[i][y]+sum[i][j] == 0 {
diff[i][j]++
diff[i][y]--
diff[x][j]--
diff[x][y]++ // 更新二维差分
}
}
}
}

// 还原二维差分矩阵对应的计数矩阵,这里用滚动数组实现
cnt := make([]int, n+1)
pre := make([]int, n+1)
for i, row := range grid {
for j, v := range row {
cnt[j+1] = cnt[j] + pre[j+1] - pre[j] + diff[i][j]
if cnt[j+1] == 0 && v == 0 {
return false
}
}
cnt, pre = pre, cnt
}
return true
}
 Comments
On this page
2132-用邮票贴满网格图