2024-04-14
Go & Python & 算法
00

目录

1 LeetCode62 不同路径
2 LeetCode63 不同路径 Ⅱ

今日任务:

  1. LeetCode62 不同路径
  2. LeetCode63 不同路径 Ⅱ

资料来源:

  1. 代码随想录 | LeetCode62 不同路径
  2. 代码随想录 | LeetCode63 不同路径 Ⅱ

1 LeetCode62 不同路径

题目

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

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

问总共有多少条不同的路径?

示例 1:

image.png

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

示例 2:

输入:m = 3, n = 2 输出:3 解释: 从左上角开始,总共有 3 条路径可以到达右下角。 1. 向右 -> 向下 -> 向下 2. 向下 -> 向下 -> 向右 3. 向下 -> 向右 -> 向下

示例 3:

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

示例 4:

输入:m = 3, n = 3 输出:6

提示:

  • 1 <= m, n <= 100
  • 题目数据保证答案小于等于 21092 * 10 ^ 9

很经典的两层数组的动态规划,还是按照动态规划五部曲走:

  1. dp数组的含义和下标含义:从起点到达当前位置的可能的路径数量

  2. 确定递推公式:dp_array[i][j] = dp_array[i - 1][j] + dp_array[i][j - 1]

  3. dp数组初始化:第一横行和第一竖行全部初始化为1,意味着从起点到达这些点的可能路径只有1条。由于n,m可能为1,所以dp_array[0][0]也得初始化为1

  4. 确定遍历顺序:先行后列还是先列后行没有影响

  5. 举例推导:以用例1为例,它的dp数组应该是:

    1111111
    1234567
    13610152128

    可见没有问题

提示

另外,这题还有一个数论的解法。首先,对于给定的m,n,从起点走到终点一定会走m+n2m + n - 2步,而且其中一定有m1m - 1步是向下走的,别管什么时候向下走。那也就是说,这题可以转化为一个数学问题:在m+n2m + n - 2个数中,挑出m1m - 1个数,可以有多少种挑法?结果就是Cm+n2m1C^{m - 1}_{m + n - 2}

python
class Solution: def uniquePaths(self, m: int, n: int) -> int: # 初始化 dp_array = [[0] * n] * m for i in range(n): dp_array[0][i] = 1 for i in range(m): dp_array[i][0] = 1 # 开始dp for i in range(1, m): for j in range(1, n): dp_array[i][j] = dp_array[i - 1][j] + dp_array[i][j - 1] return dp_array[-1][-1]
java
class Solution { public int uniquePaths(int m, int n) { int[][] dp_array = new int[m][n]; for (int i = 0; i < m; i++) { dp_array[i][0] = 1; } for (int i = 0; i < n; i++) { dp_array[0][i] = 1; } for (int i = 1; i < m; i++) { for (int j = 1; j < n; j++) { dp_array[i][j] = dp_array[i - 1][j] + dp_array[i][j - 1]; } } return dp_array[m - 1][n - 1]; } }
go
func uniquePaths(m int, n int) int { dpArray := make([][]int, m) for i := range dpArray { dpArray[i] = make([]int, n) dpArray[i][0] = 1 } for i := range dpArray[0] { dpArray[0][i] = 1 } for i := 1; i < m; i++ { for j := 1; j < n; j++ { dpArray[i][j] = dpArray[i - 1][j] + dpArray[i][j - 1] } } return dpArray[m - 1][n - 1] }

2 LeetCode63 不同路径 Ⅱ

题目

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

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

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 1 和 0 来表示。

示例 1:

image.png

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

示例 2:

输入:obstacleGrid = [[0,1],[0,0]] 输出:1

提示:

  • m == obstacleGrid.length
  • n == obstacleGrid[i].length
  • 1 <= m, n <= 100
  • obstacleGrid[i][j] 为 0 或 1

不是,这题给的用例也太坑,完全看不出来这二维数组是按行存的还是按列存的气死个人。

这题的思路和上面的62是完全一致的,但是有的地方不一样。一方面是遍历dp数组的过程中,如果遇到障碍,说明这块是永远不可抵达的,所以要将其置为0;另一方面是初始化dp数组的过程中,如果遇到障碍,那么从这个障碍之后的所有位置都要置0,因为初始化的那一行/列本来就只有一条路,有个障碍在那里直接就把这一条路堵死了。

python
from typing import List class Solution: def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int: if obstacleGrid[0][0] == 1: return 0 # 这题的obstacleGrid是按列存的 obstacleGrid[i]代表的是一列的数据 注意 # 初始化 for i in range(1, len(obstacleGrid)): # 因为是一旦遇到障碍 后面赋值为0 所以不能从头开始循环 否则下一个初始化会有问题 if obstacleGrid[i][0] == 0: obstacleGrid[i][0] = 1 else: # 一旦遇到障碍 后面全部赋值为0 obstacleGrid[i][0] = 0 if i < len(obstacleGrid) - 1: obstacleGrid[i + 1][0] = 1 for i in range(len(obstacleGrid[0])): if obstacleGrid[0][i] == 0: obstacleGrid[0][i] = 1 else: obstacleGrid[0][i] = 0 if i < len(obstacleGrid[0]) - 1: obstacleGrid[0][i + 1] = 1 for i in range(1, len(obstacleGrid)): for j in range(1, len(obstacleGrid[0])): if obstacleGrid[i][j] == 1: obstacleGrid[i][j] = 0 else: obstacleGrid[i][j] = obstacleGrid[i - 1][j] + obstacleGrid[i][j - 1] return obstacleGrid[-1][-1]
java
class Solution { public int uniquePathsWithObstacles(int[][] obstacleGrid) { if (obstacleGrid[0][0] == 1) return 0; for (int i = 1; i < obstacleGrid.length; i++) { if (obstacleGrid[i][0] == 0) { obstacleGrid[i][0] = 1; } else { obstacleGrid[i][0] = 0; if (i < obstacleGrid.length - 1) { obstacleGrid[i + 1][0] = 1; } } } for (int i = 0; i < obstacleGrid[0].length; i++) { if (obstacleGrid[0][i] == 0) { obstacleGrid[0][i] = 1; } else { obstacleGrid[0][i] = 0; if (i < obstacleGrid[0].length - 1) { obstacleGrid[0][i + 1] = 1; } } } for (int i = 1; i < obstacleGrid.length; i++) { for (int j = 1; j < obstacleGrid[0].length; j++) { if (obstacleGrid[i][j] == 0) { obstacleGrid[i][j] = obstacleGrid[i - 1][j] + obstacleGrid[i][j - 1]; } else { obstacleGrid[i][j] = 0; } } } return obstacleGrid[obstacleGrid.length - 1][obstacleGrid[0].length - 1]; } }
go
func uniquePathsWithObstacles(obstacleGrid [][]int) int { if obstacleGrid[0][0] == 1 { return 0 } for i := 1; i < len(obstacleGrid); i++ { if obstacleGrid[i][0] == 1 { obstacleGrid[i][0] = 0 if i < len(obstacleGrid) - 1 { obstacleGrid[i+1][0] = 1 } } else { obstacleGrid[i][0] = 1 } } for i := 0; i < len(obstacleGrid[0]); i++ { if obstacleGrid[0][i] == 1 { obstacleGrid[0][i] = 0 if i < len(obstacleGrid[0]) - 1 { obstacleGrid[0][i + 1] = 1 } } else { obstacleGrid[0][i] = 1 } } for i := 1; i < len(obstacleGrid); i++ { for j := 1; j < len(obstacleGrid[0]); j++ { if obstacleGrid[i][j] == 1 { obstacleGrid[i][j] = 0 } else { obstacleGrid[i][j] = obstacleGrid[i - 1][j] + obstacleGrid[i][j - 1] } } } return obstacleGrid[len(obstacleGrid) - 1][len(obstacleGrid[0]) - 1] }

本文作者:御坂19327号

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!