题目:
给你一个 m x n 的二进制矩阵 grid ,每个格子要么为 0 (空)要么为 1 (被占据)。
给你邮票的尺寸为 stampHeight x stampWidth 。我们想将邮票贴进二进制矩阵中,且满足以下 限制 和 要求 :
- 覆盖所有 空 格子。
- 不覆盖任何 被占据 的格子。
- 我们可以放入任意数目的邮票。
- 邮票可以相互有 重叠 部分。
- 邮票不允许 旋转 。
- 邮票必须完全在矩阵 内 。
如果在满足上述要求的前提下,可以放入邮票,请返回 true ,否则返回 **false 。
算法:
方法一:二维前缀和+二维差分
思路:因为邮票可以覆盖,我们不妨选择能放则放的策略,格子为1、放了邮票出界不能放,格子为0可以放,我们如何判断能不能放?放了邮票又如何标记放了呢?
1.如何判断一个位置能不能放邮票?
能不能放是判断邮票的矩形区域内有没有1(这里的1不包括,也不需要包括我们新贴邮票导致的1),我们可以通过二维前缀和计算二维差分数组,计算邮票区域内的差分是否为0,为0则可以放邮票,在O(1)时间内完成判断。
2.放了邮票如何标记呢?
本质上是在邮票的区域内对所有格子做+1操作,假设邮票的左上角坐标为(r1,c1),右下角坐标为(r2,c2),则对二维差分数组diff执行update操作即可:
diff[r1,c1]++,diff[r2 + 1,c2 + 1]++,diff[r2 + 1, c1] --, diff[r1, c2 + 1]--
3.最后如何判断格子能否用 邮票占满?
放置n次邮票,每次邮票区域内的格子+1,要求的就是贴完邮票的数组,通过差分数组,计算原始数组(原始数组的差分数组的前缀和数组)(每个格子的数字表示被贴邮票占据的次数),如果原始数组中有一个位置为0则意味着无法覆盖。
func possibleToStamp(grid [][]int, stampHeight int, stampWidth int) bool {
m, n := len(grid), len(grid[0])
diff := make([][]int, m + 2)
prefixSum := make([][]int, m + 1)
diff[0] = make([]int, n + 1)
prefixSum[0] = make([]int, n + 1)
// 构造前缀和数组,需要前缀和数组构造差分数组
for i := range grid {
diff[i + 1] = make([]int, n + 1)
prefixSum[i + 1] = make([]int, n + 1)
for j := range grid[i] {
prefixSum[i + 1][j + 1] = prefixSum[i + 1][j] + prefixSum[i][j + 1] - prefixSum[i][j] + grid[i][j]
}
}
// fmt.Println(diff)
// 遍历grid,放邮票
for i := range grid {
for j := range grid[i] {
// 可以放邮票吗,邮票区域的前置和是否为0
if grid[i][j] == 0 {
// x,y不需要再加1啦,等效于已经加了
x := i + stampHeight
y := j + stampWidth
if x <= m && y <= n && prefixSum[x][y] - prefixSum[i][y] - prefixSum[x][j] + prefixSum[i][j] == 0 {
diff[i][j] ++
diff[x][j] --
diff[i][y] --
diff[x][y] ++
}
}
}
}
// 差分求前缀和,得到原始数组,还有为0的网格就是没有被邮票填充的
// 1.不适用滚动数组
// newGrid := make([][]int, m + 1)
// newGrid[0] = make([]int, n + 1)
// for i, row := range grid {
// newGrid[i + 1] = make([]int, n + 1)
// for j, v := range row {
// newGrid[i + 1][j + 1] = newGrid[i + 1][j] + newGrid[i][j + 1] - newGrid[i][j] + diff[i][j]
// if newGrid[i + 1][j + 1] == 0 && v == 0 {
// return false
// }
// }
// }
// 2.使用滚动数组
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
}