解法一:自顶向下的递归dp + 备忘录
一般来说,让你在二维矩阵中求最优化问题(最大值或者最小值),肯定需要递归 + 备忘录,也就是动态规划。
我们想计算从起点 D
到达 B
的最小路径和,那你说怎么才能到达 B
呢?
题目说了只能向右或者向下走,所以只有从 A
或者 C
走到 B
。
怎么知道从 A
走到 B
才能使路径和最小,而不是从 C
走到 B
呢?
从 D
走到 A
的最小路径和是 6,而从 D
走到 C
的最小路径和是 8,6 小于 8,所以一定要从 A
走到 B
才能使路径和最小。
换句话说,我们把「从 D
走到 B
的最小路径和」这个问题转化成了「从 D
走到 A
的最小路径和」和 「从 D
走到 C
的最小路径和」这两个问题。
这不就是状态转移方程吗?
dp
函数定义为:从左上角位置 (0, 0)
走到位置 (i, j)
的最小路径和为 dp(grid, i, j)
。
这样,dp(grid, i, j)
的值由 dp(grid, i - 1, j)
和 dp(grid, i, j - 1)
的值转移而来:
dp(grid, i, j) = min( dp(grid, i-1, j), dp(grid, i, j-1), ) + grid[i][j]
var memo [][]int
func minPathSum(grid [][]int) int {
m := len(grid)
n := len(grid[0])
// 构造备忘录,初始值全部设为 -1
memo = make([][]int, m)
for i := range memo {
memo[i] = make([]int, n)
for j := range memo[i] {
memo[i][j] = -1
}
}
return dp(grid, m-1, n-1)
}
func dp(grid [][]int, i, j int) int {
// base case
if i == 0 && j == 0 {
return grid[0][0]
}
if i < 0 || j < 0 {
return int(^uint(0) >> 1) // Integer.MAX_VALUE equivalent in Go
}
// 避免重复计算
if memo[i][j] != -1 {
return memo[i][j]
}
// 将计算结果记入备忘录
memo[i][j] = min(
dp(grid, i-1, j),
dp(grid, i, j-1),
) + grid[i][j]
return memo[i][j]
}
func min(a, b int) int {
if a < b {
return a
}
return b
}