动态规划解二维坐标问题
动态规划类题目分为两大类
- 求最优解
- 统计方案数
动态规划中当前问题的最优解取决于子问题的最优解,当前问题的方案数取决于子问题的方案数
本文主要focus使用动态规划解决二维坐标中的状态转移问题。
prac63-不同路径II
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
输入:
[
[0,0,0],
[0,1,0],
[0,0,0]
]
输出: 2
解释:
3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
- 向右 -> 向右 -> 向下 -> 向下
- 向下 -> 向下 -> 向右 -> 向右
- 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/unique-paths-ii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
由于题目描述中机器人只能向下/向右移动一步,因此当前状态仅与之前状态有关。
使用dp[i][j]表示从坐标(0,0)到坐标(i,j)的路径总数,如果坐标(i,j)可行,则
dp[i][j] = dp[i-1][j] + dp[i][j-1]
此时时间复杂度和空间复杂度均为O(mn),官方采用滚动数组思想进行优化,将空间复杂度优化为O(m)
class Solution:
"""动态规划"""
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
n = len(obstacleGrid)
m = len(obstacleGrid[0])
dp = [0]*m
if obstacleGrid[0][0] == 0:
dp[0] = 1
else:
dp[0] = 0
for i in range(n):
for j in range(m):
if obstacleGrid[i][j] == 1:
dp[j] = 0
continue
if j-1 >= 0:
dp[j] += dp[j-1]
return dp[m-1]
if __name__ == '__main__':
obstacleGrid = [
[0,0,0],
[0,1,0],
[0,0,0]
]
sl = Solution()
print(sl.uniquePathsWithObstacles(obstacleGrid)) # console:2
滚动数组思想
滚动数组思想即通过固定的几个存储空间,来达到压缩存储空间的作用。
就以prac63题为例,考虑到每个dp[i][j]都仅和dp[i-1][j]和dp[i][j-1]有关,我们将dp[0][j]状态存在二维表中,接着递推dp[1][j],再接着递推dp[2][j]的状态时,保存dp[0][1]的状态已经没有保存意义了,所以直接保存dp[2][j]的状态保存到二维表的第0行,以此类推,使数组空间得到循环利用。