"斐波那契,动态规划开始的地方"
开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第12天,点击查看活动详情
63. 不同路径 II 题目描述:一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。现在考虑网格中有障碍物,那么从左上角到右下角将会有多少条不同的路径?网格中的障碍物和空位置分别用 1 和 0 来表示。
| 示例 | 看图 |
|---|---|
| 输入: ; 输出: ; 解释: x 网格的正中间有一个障碍物。从左上角到右下角一共有 条不同的路径: 1. 向右 -> 向右 -> 向下 -> 向下; 2. 向下 -> 向下 -> 向右 -> 向右 |
中规中矩的动态规划
本题目与 # 62. 不同路径 的最优子结构相似,只是要额外处理一下"障碍物"。
1、确定 dp 状态数组
定义 是走到 节点的不同路径数,其中,
。
2、确定 dp 状态方程
当没有"障碍物"时(),;
当存在"障碍物"时(),,因为没有一条路能到达这里。
3、确定 dp 初始状态
当 时,对于 第一个 , 且 ,那么
-
,都有
-
,都有
当 时,对于 第一个 , 且 ,那么
-
,都有
-
,都有
4、确定遍历顺序
-
第一层循环从 到 ;
-
第二层循环从 到 。
NOTE: 先遍历 后遍历 ,或者先遍历 后遍历 均可。
5、确定最终返回值
回归到状态定义中, 是走到 节点的不同路径数。
6、代码示例
/**
* 空间复杂度 O(m*n)
* 时间复杂度 O(m*n)
*/
function uniquePathsWithObstacles(obstacleGrid: number[][]): number {
const m = obstacleGrid.length;
const n = obstacleGrid[0].length;
const dp = Array.from({ length: m }, () => new Array(n).fill(0));
for(let i = 0; i < m; i++) {
if(obstacleGrid[i][0]) break;
dp[i][0] = 1;
}
for(let j = 0; j < n; j++) {
if(obstacleGrid[0][j]) break;
dp[0][j] = 1;
}
for(let i = 1; i < m; i++) {
for(let j = 1; j < n; j++) {
if (obstacleGrid[i][j]) continue;
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m - 1][n - 1];
}
调整遍历顺序
/**
* 空间复杂度 O(m*n)
* 时间复杂度 O(m*n)
*/
function uniquePathsWithObstacles(obstacleGrid: number[][]): number {
const m = obstacleGrid.length;
const n = obstacleGrid[0].length;
const dp = Array.from({ length: m }, () => new Array(n).fill(0));
for(let i = 0; i < m; i++) {
if(obstacleGrid[i][0]) break;
dp[i][0] = 1;
}
for(let j = 0; j < n; j++) {
if(obstacleGrid[0][j]) break;
dp[0][j] = 1;
}
for(let j = 1; j < n; j++) {
for(let i = 1; i < m; i++) {
if (obstacleGrid[i][j]) continue;
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m - 1][n - 1];
}