返回
秒懂高效动态规划题:“雨水收集”的秘诀
前端
2023-05-19 20:02:10
动态规划揭秘:二维接雨水算法
引言
动态规划是一种强大的算法技术,它以“以终为始”的思想为基础,将问题分解成更小的子问题,逐步求解,最终解决整个问题。在这个博客中,我们将通过一个典型的“二维接雨水”算法来深入剖析动态规划的精髓。
问题陈述
在一个二维网格中,每个单元格的高度代表它可以接住的雨水量。雨水只能从上向下流动,不会流到比自己高的单元格中。我们的目标是计算出这个二维网格中可以接住的最大雨水量。
动态规划求解
以终为始
动态规划的核心思想是“以终为始”。我们从目标(最大雨水量)出发,逐步分解问题。
分解子问题
我们把问题分解成两个子问题:
- 从左到右计算每一行可以接住的最大雨水量。
- 从上到下计算每一列可以接住的最大雨水量。
递推关系
对于每一个单元格,它的最大雨水量取决于相邻单元格的高度。因此,我们可以建立递推关系:
dp[i][j] = max(0, min(dp[i-1][j], dp[i][j-1]) - height[i][j])
其中:
dp[i][j]
表示第i
行第j
列单元格可以接住的最大雨水量。height[i][j]
表示第i
行第j
列单元格的高度。
具体步骤
- 初始化二维数组
dp
,每个单元格的值为 0。 - 从左到右遍历每一行,根据递推关系更新
dp
。 - 从上到下遍历每一列,根据递推关系更新
dp
。 - 最后,
dp
中的每个单元格的值就是该单元格可以接住的最大雨水量。
代码示例
def max_rainwater(grid):
m, n = len(grid), len(grid[0])
dp = [[0] * n for _ in range(m)]
# 从左到右计算每一行
for i in range(m):
for j in range(1, n):
dp[i][j] = max(0, dp[i][j-1] - grid[i][j])
# 从上到下计算每一列
for j in range(n):
for i in range(1, m):
dp[i][j] = max(0, dp[i-1][j] - grid[i][j])
# 返回最大雨水量
return max([max(row) for row in dp])
优点与缺点
优点:
- 时间复杂度较低,为 O(m*n)。
- 空间复杂度也较低,为 O(m*n)。
- 易于理解和实现。
缺点:
- 对于非常大的网格,空间复杂度可能成为限制因素。
- 在某些特殊情况下,算法的效率可能较低。
常见问题解答
1. 什么是动态规划?
动态规划是一种将问题分解成更小子问题的算法技术,然后逐步解决这些子问题,最终解决整个问题。
2. 为什么使用动态规划来解决二维接雨水问题?
因为二维接雨水问题具有重叠子问题的特点,即某个单元格可以接住的最大雨水量取决于相邻单元格的高度,而这些单元格的高度又是由它们相邻的单元格决定的。
3. 如何初始化二维数组 dp?
每个单元格的值都初始化为 0,表示它最初不能接住任何雨水。
4. 算法的时间复杂度是多少?
O(m*n),其中 m 是网格的行数,n 是网格的列数。
5. 算法的空间复杂度是多少?
O(m*n),因为需要一个二维数组 dp 来存储计算结果。