给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
**说明:**每次只能向下或者向右移动一步。
示例:
输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。
解题思路
实际上这个问题和之前的问题Leetcode 200:岛屿的个数(最详细的解法!!!) 和Leetcode 79:单词搜索(最详细的解法!!!) 很类似。
这个问题通过递归很好解决,对于上面的例子来说。我们要知道1
开始的最小路径,那么我们只要知道周围1
、3
的最小路径即可,以此类推下去即可。我们这里的边界条件就是我们递归到底的时候,也就是
self.r, self.c = len(grid), len(grid[0])
row == self.r - 1 and col == self.c - 1
并且我们还要考虑右上角和左下角的情况,所以我们写出这样的代码。
class Solution:
def minPathSum(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
if not grid:
return 0
self.r, self.c = len(grid), len(grid[0])
return self._minPathSum(0, 0, grid)
def _minPathSum(self, row, col, grid):
if row == self.r - 1 and col == self.c - 1:
return grid[row][col]
if row + 1 < self.r and col + 1 < self.c:
return grid[row][col] + min(self._minPathSum(row, col + 1, grid), self._minPathSum(row + 1, col, grid))
if row + 1 < self.r:
return grid[row][col] + self._minPathSum(row + 1, col, grid)
if col + 1 < self.c:
return grid[row][col] + self._minPathSum(row, col + 1, grid)
但是这样做存在着大量的重复运算(在哪呢?)。我们可以通过记忆化搜索的方式来优化上面的问题。
class Solution:
def minPathSum(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
if not grid:
return 0
self.r, self.c = len(grid), len(grid[0])
mem = [[0]*self.c for _ in range(self.r)]
return self._minPathSum(0, 0, grid, mem)
def _minPathSum(self, row, col, grid, mem):
if row == self.r - 1 and col == self.c - 1:
return grid[row][col]
if mem[row][col]:
return mem[row][col]
if row + 1 < self.r and col + 1 < self.c:
mem[row][col] = grid[row][col] + min(self._minPathSum(row, col + 1, grid, mem),\
self._minPathSum(row + 1, col, grid, mem))
return mem[row][col]
if row + 1 < self.r:
mem[row][col] = grid[row][col] + self._minPathSum(row + 1, col, grid, mem)
return mem[row][col]
if col + 1 < self.c:
mem[row][col] = grid[row][col] + self._minPathSum(row, col + 1, grid, mem)
return mem[row][col]
实际上我们这里通过迭代的方式去写这个问题更加简洁。无非就是,先将第一行和第一列元素分别向后累加。
[
[1,4,5],
[2,5,1],
[6,2,1]
]
然后考虑,第二行和第二列以及之后的元素问题,例如上面列中的中间元素,我们要考虑左边加过来和上面加下来,那个值更小min(4+5,2+5)
,同理以此类推。
class Solution:
def minPathSum(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
if not grid:
return 0
r, c = len(grid), len(grid[0])
mem = [[0]*self.c for _ in range(self.r)]
mem[0][0] = grid[0][0]
for i in range(1, c):
mem[0][i] = grid[0][i] + mem[0][i - 1]
for i in range(1, r):
mem[i][0] = grid[i][0] + mem[i - 1][0]
for i in range(1, r):
for j in range(1, c):
mem[i][j] = grid[i][j] + min(mem[i - 1][j], mem[i][j - 1])
return mem[r - 1][c - 1]
当然对于这个我问题,我们也可以如之前问题Leetcode 120:三角形最小路径和(最详细的解法!!!) 一样反过来思考。
class Solution:
def minPathSum(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
if not grid:
return 0
self.r, self.c = len(grid), len(grid[0])
mem = [[0]*self.c for _ in range(self.r)]
return self._minPathSum(grid,0, 0, mem)
def _minPathSum(self, grid, row, col, mem):
if row == self.r - 1 and col == self.c - 1:
mem[row][col] = grid[row][col]
return mem[row][col]
if row == self.r - 1:
mem[row][col] = grid[row][col] + self._minPathSum(grid, row, col + 1, mem)
return mem[row][col]
if col == self.c - 1:
mem[row][col] = grid[row][col] + self._minPathSum(grid, row + 1, col, mem)
return mem[row][col]
if not mem[row][col]:
mem[row][col] = grid[row][col] + min(self._minPathSum(grid, row, col + 1, mem),
self._minPathSum(grid, row + 1, col, mem))
return mem[row][col]
这个问题也可以使用最短路径算法解决,但是有点大材小用了。
我将该问题的其他语言版本添加到了我的GitHub Leetcode
如有问题,希望大家指出!!!