2132. 用邮票贴满网格图

205 阅读3分钟

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

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

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

如果在满足上述要求的前提下,可以放入邮票,请返回 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
}