LeetCode 题解工作台
统计农场中肥沃金字塔的数目
有一个 矩形网格 状的农场,划分为 m 行 n 列的单元格。每个格子要么是 肥沃的 (用 1 表示),要么是 贫瘠 的(用 0 表示)。网格图以外的所有与格子都视为贫瘠的。 农场中的 金字塔 区域定义如下: 区域内格子数目 大于 1 且所有格子都是 肥沃的 。 金字塔 顶端 是这个金字塔 最上方 的…
3
题型
4
代码语言
3
相关题
当前训练重点
困难 · 状态·转移·动态规划
答案摘要
我们定义 表示以 $(i, j)$ 为顶点的金字塔的最大高度,那么有如下状态转移方程: $$
Interview AiBoxInterview AiBox 实时 AI 助手,陪你讲清 状态·转移·动态规划 题型思路
题目描述
有一个 矩形网格 状的农场,划分为 m 行 n 列的单元格。每个格子要么是 肥沃的 (用 1 表示),要么是 贫瘠 的(用 0 表示)。网格图以外的所有与格子都视为贫瘠的。
农场中的 金字塔 区域定义如下:
- 区域内格子数目 大于
1且所有格子都是 肥沃的 。 - 金字塔 顶端 是这个金字塔 最上方 的格子。金字塔的高度是它所覆盖的行数。令
(r, c)为金字塔的顶端且高度为h,那么金字塔区域内包含的任一格子(i, j)需满足r <= i <= r + h - 1且c - (i - r) <= j <= c + (i - r)。
一个 倒金字塔 类似定义如下:
- 区域内格子数目 大于
1且所有格子都是 肥沃的 。 - 倒金字塔的 顶端 是这个倒金字塔 最下方 的格子。倒金字塔的高度是它所覆盖的行数。令
(r, c)为金字塔的顶端且高度为h,那么金字塔区域内包含的任一格子(i, j)需满足r - h + 1 <= i <= r且c - (r - i) <= j <= c + (r - i)。
下图展示了部分符合定义和不符合定义的金字塔区域。黑色区域表示肥沃的格子。

给你一个下标从 0 开始且大小为 m x n 的二进制矩阵 grid ,它表示农场,请你返回 grid 中金字塔和倒金字塔的 总数目 。
示例 1:

输入:grid = [[0,1,1,0],[1,1,1,1]] 输出:2 解释: 2 个可能的金字塔区域分别如上图蓝色和红色区域所示。 这个网格图中没有倒金字塔区域。 所以金字塔区域总数为 2 + 0 = 2 。
示例 2:

输入:grid = [[1,1,1],[1,1,1]] 输出:2 解释: 金字塔区域如上图蓝色区域所示,倒金字塔如上图红色区域所示。 所以金字塔区域总数目为 1 + 1 = 2 。
示例 3:

输入:grid = [[1,0,1],[0,0,0],[1,0,1]] 输出:0 解释: 网格图中没有任何金字塔或倒金字塔区域。
示例 4:

输入:grid = [[1,1,1,1,0],[1,1,1,1,1],[1,1,1,1,1],[0,1,0,0,1]] 输出:13 解释: 有 7 个金字塔区域。上图第二和第三张图中展示了它们中的 3 个。 有 6 个倒金字塔区域。上图中最后一张图展示了它们中的 2 个。 所以金字塔区域总数目为 7 + 6 = 13.
提示:
m == grid.lengthn == grid[i].length1 <= m, n <= 10001 <= m * n <= 105grid[i][j]要么是0,要么是1。
解题思路
方法一:动态规划
我们定义 表示以 为顶点的金字塔的最大高度,那么有如下状态转移方程:
因此,我们可以从下往上、从左往右遍历网格,计算出所有的 ,并累加所有的 即可得到正金字塔的个数。
接下来,我们考虑倒金字塔的个数。与金字塔类似,我们定义 表示以 为顶点的倒金字塔的最大高度,那么有如下状态转移方程:
因此,我们可以从上往下、从左往右遍历网格,计算出所有的 ,并累加所有的 即可得到倒金字塔的个数。
最后,正金字塔的个数加上倒金字塔的个数即为答案。实际代码中,我们可以只用一个二维数组 即可。
时间复杂度 ,空间复杂度 。其中 和 分别为网格的行数和列数。
class Solution:
def countPyramids(self, grid: List[List[int]]) -> int:
m, n = len(grid), len(grid[0])
f = [[0] * n for _ in range(m)]
ans = 0
for i in range(m - 1, -1, -1):
for j in range(n):
if grid[i][j] == 0:
f[i][j] = -1
elif not (i == m - 1 or j == 0 or j == n - 1):
f[i][j] = min(f[i + 1][j - 1], f[i + 1][j], f[i + 1][j + 1]) + 1
ans += f[i][j]
for i in range(m):
for j in range(n):
if grid[i][j] == 0:
f[i][j] = -1
elif i == 0 or j == 0 or j == n - 1:
f[i][j] = 0
else:
f[i][j] = min(f[i - 1][j - 1], f[i - 1][j], f[i - 1][j + 1]) + 1
ans += f[i][j]
return ans
复杂度分析
| 指标 | 值 |
|---|---|
| 时间 | Depends on the final approach |
| 空间 | Depends on the final approach |
面试官常问的追问
外企场景- question_mark
They want you to stop brute forcing heights and define a DP state that stores the tallest pyramid centered at each cell.
- question_mark
They are checking whether you notice that every apex contributes multiple pyramids when its maximum height is greater than 2.
- question_mark
They expect you to exploit symmetry for inverse pyramids instead of writing a slow second validation routine.
常见陷阱
外企场景- error
Counting height 1 cells as pyramids, which inflates the result immediately on dense grids.
- error
Using width validation for every candidate height, which turns this matrix problem into a slow repeated scan.
- error
Forgetting boundary behavior in the transition, especially that edge cells cannot grow because one side of the next row is missing.
进阶变体
外企场景- arrow_right_alt
Return the number of upright pyramids only, which removes the second pass but keeps the same DP transition.
- arrow_right_alt
Return the maximum pyramid height anywhere in the grid instead of the total count, which uses the same state without summation.
- arrow_right_alt
Allow queries that flip cells between fertile and barren, which breaks the static DP and would need a very different update strategy.