不同路径Ⅰ
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。问总共有多少条不同的路径?
例如,下图是一个7 x 3 的网格。有多少可能的路径?
示例 1:
输入: m = 3, n = 2输出: 3解释:从左上角开始,总共有 3 条路径可以到达右下角。1. 向右 -> 向右 -> 向下2. 向右 -> 向下 -> 向右3. 向下 -> 向右 -> 向右
示例 2:
输入: m = 7, n = 3输出: 28
提示:
- 1 <= m, n <= 100
- 题目数据保证答案小于等于 2 * 10 ^ 9
题目给定一个二维的网格图,🤖从左上角[0, 0]位置出发,最终要到达右下角的[m, n]处,需要找到有多少条路径能够完成这样的过程。如果更泛化一点讲,给定起点位置为[i, j],目的位置为[p, q],找所有可能的路径的数量。因此,我们可以从全局缩小到某一个局部来思考,到达位置[i, j]的路径有多少。如下所示:
由于题目规定了只能向下或者向右移动一步,因此,能够到达[i, j]位置处的可能是[i, j-1]向右走了一步,也可能是[i-1, j]向下走了一步。总之,[i, j]的状态依赖于[i, j-1]和[i-1, j],如果知道了[i, j-1]和[i-1, j]位置处对应的路径数量,那么到达[i, j]处的路径数量就这两者数量之和。根据状态转移的关系,显然该题可以使用动态规划来解。
假设dp数组dp[i][j]表示到达[i, j]位置处的路径数量,那么需要考虑两种情况:
- 对于边界的网格来说,即第一行或者第一列的网格来说,可能的路径只能是向右和向下,因此:
- 对于其他位置来说,动态转移方程为:
遍历所有的网格,就可以获取到到达任意一处的路径数量。
class Solution{public int uniquePaths(int m, int n) {int[][] dp = new int[m][n];for(int i = 0; i < m; i++){for(int j = 0; j < n; j++){if(i == 0 || j == 0){dp[i][j] = 1;} else {dp[i][j] = dp[i - 1][j] + dp[i][j - 1];}}return dp[m - 1][n - 1];}}
不同路径 II
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。
示例 1:
**
输入: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
该题相比于前一题来说,整体的过程是相同的,不同之处在于网络的路径上可能有障碍物。那么,对于除了边界之外的某一个位置[i, j]来说,可能的情况有:
- 左边的格子有障碍物,如下所示:

那么对应的动态转移方程是:
- 上边的格子有障碍物,如下所示:

对应的动态转移方程为:
- 左边和上边都是障碍物,如下所示:

那么,没有路径可以达到[i, j]处,因此。
经过上述可以分析,对于位置[i, j]来说,只要该处没有障碍物,那么动态转移方程仍然成立。
解题代码如下:
class Solution {public int uniquePathsWithObstacles(int[][] obstacleGrid) {int m = obstacleGrid.length;int n = obstacleGrid[0].length;int[][] dp = new int[m][n];for(int i = 0; i < m && obstacleGrid[i][0] == 0; i++){dp[i][0] = 1;}for(int j = 0; j < n && obstacleGrid[0][j] == 0; j++){dp[0][j] = 1;}for(int i = 1; i < m; i++){for(int j = 1; j < n; j++){if(obstacleGrid[i][j] == 0){dp[i][j] = dp[i - 1][j] + dp[i][j - 1];}}}return dp[m - 1][n - 1];}}
或者直接在两次for循环中考虑第一列、第一行和其他的情况:
class Solution {public int uniquePathsWithObstacles(int[][] obstacleGrid) {int m = obstacleGrid.length;int n = obstacleGrid[0].length;int[][] dp = new int[m][n];// row_f:行标识位,只要前面有一个格子有障碍物,值就变为true// row_c:列标识位,只要前面有一个格子有障碍物,值就变为trueboolean row_f = false, col_f = false;for(int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {if (i == 0 && col_f == false) {if (obstacleGrid[0][j] == 0) {dp[0][j] = 1;} else {dp[0][j] = 0;col_f = true;}}if (j == 0 && row_f == false) {if (obstacleGrid[i][0] == 0) {dp[i][0] = 1;} else {dp[i][0] = 0;row_f = true;}}// 只考虑格子中没有障碍物的情况if(i != 0 && j != 0 && obstacleGrid[i][j] == 0){dp[i][j] = dp[i - 1][j] + dp[i][j - 1];}}}return dp[m - 1][n - 1];}}
不同的路径Ⅲ
在二维网格 grid 上,有 4 种类型的方格:
- 1 表示起始方格。且只有一个起始方格。
- 2 表示结束方格,且只有一个结束方格。
- 0 表示我们可以走过的空方格。
- -1 表示我们无法跨越的障碍。
返回在四个方向(上、下、左、右)上行走时,从起始方格到结束方格的不同路径的数目。每一个无障碍方格都要通过一次,但是一条路径中不能重复通过同一个方格。
示例 1:
输入:[[1,0,0,0],[0,0,0,0],[0,0,2,-1]]输出:2解释:我们有以下两条路径:1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2)2. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2)
示例 2:
输入:[[1,0,0,0],[0,0,0,0],[0,0,0,2]]输出:4解释:我们有以下四条路径:1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3)2. (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3)3. (0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3),(1,3),(2,3)4. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2),(2,3)
示例 3:
输入:[[0,1],[2,0]]输出:0解释:没有一条路能完全穿过每一个空的方格一次。请注意,起始和结束方格可以位于网格中的任意位置。
提示:
- 1 <= grid.length * grid[0].length <= 20
该题和上面两题又有所不同,出发点和终点不再是固定的左上角和右下角,而是类型为1的为出发点,类型为2的为终点。并且要求通过所有无障碍的方格,对应的类型为0。允许上下左右四个方向的行走,最后统计不同路径的数目。由于题目要求寻找所有可能的路径,因此使用深搜+回溯的方法来解。
经过上面的分析可知,在深搜之前需要完成几件事:
- 寻找出发点的坐标,即标识为1的点
- 统计无障碍方格的数目,后续判断路径是否合法时使用
找到了出发点和知道了无障碍方格的数目后,便可以开始通过深搜➕回溯来寻找可能的路径。对于走到的具体方格位置[x, y]来说:
- 如果位置越界或者此方格标识为-1,则表示非法路径,直接返回0
- 如果当前方格表示为2,表示已经到达终点,判断是否走完了所有的无障碍方格
- 否则,继续走,走的方向有上下左右四种
解题代码如下:
class Solution {public int uniquePathsIII(int[][] grid) {// 起始点坐标int sx = 0, sy = 0;// 无障碍方格数int steps = 1;for (int i = 0; i < grid.length; i++){for (int j = 0; j < grid[0].length; j++){if (grid[i][j] == 1){sx = i;sy = j;continue;}if (grid[i][j] == 0)steps++;}}return dfs(sx, sy, steps, grid);}// 深搜寻找所有可能路径public int dfs(int x, int y, int steps, int[][] grid){// 如果位置越界或者碰到障碍点,直接返回0,此路不通if (x < 0 || x >= grid.length || y < 0 || y >= grid[0].length || grid[x][y] == -1)return 0;// 到达终点if (grid[x][y] == 2)// 走完所有的无障碍点,表示和一条合法的路径;否则非法return steps == 0 ? 1 : 0;// 记录状态信息,凡走过的方格不能再走grid[x][y] = -1;// 统计合法路径数量int count = 0;count += dfs(x - 1, y, steps - 1, grid) + dfs(x + 1, y, steps - 1, grid) + dfs(x, y - 1, steps - 1, grid) + dfs(x, y + 1, steps - 1, grid);// 撤销状态grid[x][y] = 0;return count;}}
