Day39【动态规划】62.不同路径、63.不同路径 II

附一个动态规划五部曲在这里

  1. 确定dp数组下标及值的含义
  2. 确定递推公式
  3. dp数组如何初始化
  4. 确定遍历顺序(遍历循环外层是啥内层是啥,遍历从左向右还是从右向左)
  5. 打印dp数组,用于 debug

62.不同路径

力扣题目链接/文章讲解 

视频讲解 

本题要用二维的 dp 数组

1、确定 dp 数组下标及值的含义 

dp[i][j]:下标 i, j 表示位于行索引为 i,列索引为 j 的位置,dp[i][j] 的值表示到达该位置的不同路径条数

2、确定递推公式

想求 dp[i][j],这个 i, j 表示机器人所处的位置,根据题目描述,可以从位置 i - 1, j 向下走到位置 i, j;也可以从位置 i, j - 1 向右走到位置 i, j

 

到达位置 i, j 的不同路径条数 dp[i][j] 为到达位置 i - 1, j 的不同路径条数 dp[i - 1][j] 加上到达位置 i, j - 1 的不同路径条数 dp[i][j - 1],即 dp[i][j] = dp[i - 1][j] + dp[i][j - 1]

在推导 dp[i][j] 的时候,一定要时刻想着 dp 数组下标及值的含义,否则容易跑偏 

3、dp 数组初始化

本题遍历更新 dp 数组需要用到左边的值和上方的值,因此需要初始化 dp 数组最左边一列和最上方一行的值

如何初始化呢,根据 dp 数组下标及值的含义,首先 dp[i][0] 一定都是1,因为从 0, 0 的位置到 i, 0 的路径只有一条,那么 dp[0][j] 也同理 

for (int i = 0; i < m; i++) dp[i][0] = 1;
for (int j = 0; j < n; j++) dp[0][j] = 1;

4、确定遍历顺序

这里要看一下递推公式 dp[i][j] = dp[i - 1][j] + dp[i][j - 1],dp[i][j] 都是从其上方和左方推导而来,那么我们从左到右从上层到下层遍历就可以了

这样就可以保证推导 dp[i][j] 的时候,dp[i - 1][j] 和 dp[i][j - 1] 一定是已经被更新后的正确数值

5、打印 dp 数组验证 

代码如下

class Solution {
public:
    int uniquePaths(int m, int n) {
        // 1、定义dp数组下标及值含义:下标表示位置,值表示到该位置的不同路径
        vector<vector<int> > dp(m, vector<int>(n));
        // 2、递推公式 dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
        // 3、初始化第一列和第一行为1
        for (int i = 0; i < m; i++) dp[i][0] = 1;
        for (int j = 0; j < n; j++) dp[0][j] = 1;
        // 4、遍历更新dp数组
        for (int i = 1; i < m; ++i)
            for (int j = 1; j < n; ++j)
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
        // 5、打印验证dp数组,略
        return dp[m - 1][n - 1];    // 根据题意,答案就是dp[m - 1][n - 1]
    }
};

63.不同路径 II

力扣题目链接/文章讲解 

视频讲解 

本题和62.不同路径类似思路大体上是相同的,还是按照动态规划五部曲分析

1、确定 dp 数组下标及值的含义 

dp[i][j]:下标 i, j 表示位于行索引为 i,列索引为 j 的位置,dp[i][j] 的值表示到达该位置的不同路径条数

2、确定递推公式

想求 dp[i][j],这个 i, j 表示机器人所处的位置,根据题目描述,可以从位置 i - 1, j 向下走到位置 i, j;也可以从位置 i, j - 1 向右走到位置 i, j

但这里需要注意一点,因为有了障碍,位置 i, j 如果有障碍的话,到达这个位置的不同路径条数一定为 0,因此,某个位置有障碍的话,dp 数组应该保持初始状态(初始状态为0),没有障碍的时候才利用递推公式更新 dp 数组

if (obstacleGrid[i][j] == 0) { // 当(i, j)没有障碍的时候,再推导dp[i][j]
    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}

3、dp 数组初始化 

和上一道题同理,本题遍历更新 dp 数组需要用到左边的值和上方的值,因此需要初始化 dp 数组最左边一列和最上方一行的值

和上一道题不同的是,因为机器人只能向右或者向下,如果某条边(指第一行或第一列)有障碍物,障碍之后(包括障碍)都是走不到的位置了,所以障碍之后(指第一行的右边或第一列的下边)的 dp 数组应该就是初始值 0

以初始化第一行的情况为例 

 本题的初始化代码为 

vector<vector<int>> dp(m, vector<int>(n, 0));
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;

4、确定遍历顺序 

从左到右从上层到下层遍历就可以了,这样就可以保证推导 dp[i][j] 的时候,dp[i - 1][j] 和 dp[i][j - 1] 一定是已经被更新后的正确数值

5、打印 dp 数组验证 

代码如下

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size();//行
        int n = obstacleGrid[0].size();//列
	    if (obstacleGrid[m - 1][n - 1] == 1 || obstacleGrid[0][0] == 1) //如果在起点或终点出现了障碍,直接返回0
            return 0;
        // 1、定义dp数组下标及值的含义
        vector<vector<int>> dp(m, vector<int>(n, 0));
        // 2、递推公式dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
        // 3、初始化,注意有障碍
        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;
        // 4、遍历更新dp数组,注意有障碍
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (obstacleGrid[i][j] == 1) continue;
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
        // 5、打印dp数组略
        return dp[m - 1][n - 1];
    }
};

回顾总结 

今天用的二维 dp 数组,两个下标的含义:用来表示所处的位置状态

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林沐华

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值