LeetCode之动态规划2之不同路径(62)、不同路径II(63)、最小路径和(64)

1、不同路径(62)

题目描述:

【中等】
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?
示例:
在这里插入图片描述

输入:m = 3, n = 7
输出:28

题目链接

思路分析

1、由于我们每一步只能从向下或者向右移动一步,因此要想走到 (i, j)(i,j),如果向下走一步,那么会从 (i-1, j)(i−1,j) 走过来;如果向右走一步,那么会从 (i, j-1)(i,j−1) 走过来。

2、那么就可以分解成重叠子问题进行求解,运用动态规划:

第1步:定义状态

dp[i][j]:表示从左上角出发到 (i,j)位置的最多路径总数

第2步:状态转移方程

dp[i][j] = dp[i-1][j] + dp[i][j-1]
  • dp[i-1][j]表示的是从上面走过来的路径条数。
  • dp[i][j-1]表示的是从左边走过来的路径条数。

第3步:初始值(边界条件)

  • 对于第一行 dp[0][j],或者第一列 dp[i][0],由于都是在边界,所以只能为 1
  • dp[0][j]==1;dp[i][0]==1

第4步:思考输出,返回值

  • 返回dp矩阵右下角值,即走到终点的不同路径总数
class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        dp=[[0]*n for _ in range(m)]
        # 初始化
        for i in range(m):
            dp[i][0]=1
        for j in range(n):
            dp[0][j]=1
        # 转移方程,进行填充
        for i in range(1,m):
            for j in range(1,n):
                dp[i][j]=dp[i-1][j]+dp[i][j-1]
        return dp[-1][-1]
  • 时间复杂度: O ( m n ) O(mn) O(mn)
  • 空间复杂度: O ( m n ) O(mn) O(mn)

第5步:思考状态压缩

  • 因为我们每次只需要 dp[i-1][j],dp[i][j-1],所以我们只要记录这两个数即可。
class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        cur = [1] * n
        for i in range(1, m):
            for j in range(1, n):
                cur[j] += cur[j-1]
        return cur[-1]

  • 时间复杂度: O ( m n ) O(mn) O(mn)
  • 空间复杂度: O ( n ) O(n) O(n)

2、不同路径II(63)

题目描述:

【中等】
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
在这里插入图片描述
网格中的障碍物和空位置分别用 1 和 0 来表示。
示例 1:
在这里插入图片描述

 输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:
3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右

题目链接

思路分析

3、最小路径和(64)

题目描述:

【中等】

给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步。

示例一
在这里插入图片描述

输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 13111 的总和最小。

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 200
  • 0 <= grid[i][j] <= 100

题目链接

思路分析

1、由于路径的方向只能是向下或向右,因此网格的第一行的每个元素只能从左上角元素开始向右移动到达,网格的第一列的每个元素只能从左上角元素开始向下移动到达,此时的路径是唯一的,因此每个元素对应的最小路径和即为对应的路径上的数字总和。

2、对于不在第一行和第一列的元素,可以从其上方相邻元素向下移动一步到达,或者从其左方相邻元素向右移动一步到达,元素对应的最小路径和等于其上方相邻元素与其左方相邻元素两者对应的最小路径和中的最小值加上当前元素的值。由于每个元素对应的最小路径和与其相邻元素对应的最小路径和有关,因此可以使用动态规划求解。

第1步:定义状态

dp[i][j]:表示从左上角出发到 (i,j)位置的最小路径和,易知dp[0][0]=grid[0][0]

第2步:状态转移方程

  • 当i=0且 j=0即矩阵的左上角元素也是出发点:dp[0][0]=grid[0][0]
  • 当 i=0且 j>0即矩阵的第一行:dp[0][j]=dp[0][j-1]+grid[0][j]
  • 当 i>0且 j=0即矩阵的第一列:dp[i][0]=dp[i-1][0]+grid[i][0]
  • 当 i>0且 j>0即矩阵非边界元素:dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i][j]

第3步:初始值(边界条件)

  • dp初始化即可

第4步:思考输出,返回值

  • 返回dp矩阵右下角值,即走到终点的最小路径和

第5步:思考状态压缩

  • 由于dp矩阵与原矩阵维度一样,因此我们可以在原矩阵上直接修改成当前位置的最小路径和即可。
class Solution:
    def minPathSum(self, grid: List[List[int]]) -> int:
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if i == j == 0: continue
                elif i == 0:  grid[i][j] = grid[i][j - 1] + grid[i][j]
                elif j == 0:  grid[i][j] = grid[i - 1][j] + grid[i][j]
                else: grid[i][j] = min(grid[i - 1][j], grid[i][j - 1]) + grid[i][j]
        return grid[-1][-1]
  • 时间复杂度: O ( m n ) O(mn) O(mn):遍历整个矩阵
  • 空间复杂度: O ( 1 ) O(1) O(1):直接修改原矩阵,不使用额外空间

猜你喜欢

转载自blog.csdn.net/weixin_45666566/article/details/113487912