算法力扣刷题记录 七十七【63. 不同路径 II】

前言

动态规划第6篇。记录 七十七【63. 不同路径 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

二、尝试实现

分析题目,确定方法

  1. 记录 七十四【62.不同路径】与这道题类似,只不过本题的网格中多了障碍物,无法通过。这属于“跳房子”、“爬楼梯”类型的题目,所以是状态转移类型,有状态转移公式,应该用动态规划完成。

动态规划实现

  1. 第一步:确定dp数组含义和下标含义。定义一个二维数组dp[i][j],对应网络二维图形。数组含义:到第i+1行第j+1列有dp[i][j]条路径
  2. 第二步:确定状态转移公式。dp[i][j] = dp[i-1][j] + dp[i][j-1];第一个加数代表向下走一步,第二个加数代表向右走一步。但是障碍物的地方怎么处理呢?
  • 如下图,蓝色方格和黄色方格是紧挨着障碍物的网格。其余位置的上方和左方没有障碍物,所以递推公式同样适用。
  • 蓝色方格只能从上放向下一步,左侧无法通过;黄色方格只能从左向右一步,上方无法通过。
  • 如此dp[1][1]障碍物的位置dp值为0,作为加数并不影响状态方程。
  • 所以:对于有障碍物的网格,dp = 0.
    在这里插入图片描述
  1. 第三步:确定初始化。没有障碍物时,初始化第一行和第一列,dp =1;有障碍物时:
  • 如果是空位置,dp可以和前一个相等。
  • 如果遇到障碍物,dp =0;
  • 所以逻辑如下:第一行和第一列同一个道理。
  • (可能初次做题会按照没有障碍物的情况初始化,但是提交用例不通过,就需要重新改正)
	for(int i = 0;i < m;i++){//初始化第一列
            if(i > 0 && obstacleGrid[i][0] == 0){//没有障碍物的地方
                    dp[i][0] = dp[i-1][0];
            }else if(obstacleGrid[i][0] == 1){
                dp[i][0] = 0;
            }else{
                dp[i][0] = 1;
            }
        }

在这里插入图片描述
4. 遍历顺序:从递推公式知道,从前往后,从上到下。

代码实现

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int result;//返回值
        int m = obstacleGrid.size();//有m行
        int n = obstacleGrid[0].size();//有n列
        //定义dp[i][j]数组。含义是到第i+1行第j+1列有有dp[i][j]条路径。遇到障碍物的地方dp[i][j]=0.代表该位置无妨通过。
        vector<vector<int>> dp(m,vector<int> (n,0));//建立数组是初始化为0;

        //dp初始化.
        for(int i = 0;i < m;i++){//初始化第一列
            if(i > 0 && obstacleGrid[i][0] == 0){//没有障碍物的地方
                    dp[i][0] = dp[i-1][0];
            }else if(obstacleGrid[i][0] == 1){
                dp[i][0] = 0;
            }else{
                dp[i][0] = 1;
            }
        }
        for(int i = 0;i < n;i++){//初始化第一行
            if(i >0 && obstacleGrid[0][i] == 0){//没有障碍物的地方
                dp[0][i] = dp[0][i-1];
            }else if(obstacleGrid[0][i] == 1){
                dp[0][i] = 0;
            }else{
                dp[0][i] = 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];
                }else{
                    dp[i][j] = 0;
                }
            }
        }
        return dp[m-1][n-1];
    }
};

改进dp数组

  1. 记录 七十四【62.不同路径】改进dp数组变成一维。那么这里也尝试一下。
class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int result;//返回值
        int m = obstacleGrid.size();//有m行
        int n = obstacleGrid[0].size();//有n列
        //定义dp[i]数组。滚动数组,每一行都更新。含义是到某一行第i+1列有dp[i][j]条路径。
        vector<int> dp(n,0);//建立数组是初始化为0;

        //dp初始化.
        for(int i = 0;i < n;i++){//初始化第一行
            if(i >0 && obstacleGrid[0][i] == 0){//没有障碍物的地方
                dp[i] = dp[i-1];
            }else if(obstacleGrid[0][i] == 1){
                dp[i] = 0;
            }else{
                dp[i] = 1;
            }
        }

        //遍历顺序
        for(int i = 1;i < m;i++){//外层是行
            for(int j = 0;j < n;j++){//内层是列
                if(j > 0 && obstacleGrid[i][j] == 0){
                    dp[j] += dp[j-1];
                }else if(obstacleGrid[i][j] == 1){
                    dp[j]= 0;
                }
            }
        }
        return dp[n-1];
    }
};

三、参考学习

63. 不同路径 II 参考学习链接

学习内容

  1. 思路和操作都是一致的,但是代码处理上可以改进。
  2. 改进初始化:因为在vector<vector> dp(m,vector (n,0));//方格初始化是0。所以一些赋值0的操作可以跳过,维持原状
for(int i = 0;i < m && obstacleGrid[i][0] == 0;i++){//初始化第一列
	dp[i][0] = 1;
}
for(int i = 0;i < n && obstacleGrid[0][i] == 0;i++){//初始化第一行
	dp[0][i] = 1;
}
  1. 递推公式:可以把else不要。
	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];
                }else{
                    dp[i][j] = 0;
                }
            }
        }
改成
		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];
            }
        }
  1. 同理,把二、改进dp数组代码也重新改下初始化
class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int result;//返回值
        int m = obstacleGrid.size();//有m行
        int n = obstacleGrid[0].size();//有n列
        //定义dp[i]数组。滚动数组,每一行都更新。含义是到某一行第i+1列有dp[i][j]条路径。
        vector<int> dp(n,0);//建立数组是初始化为0;

        //dp初始化.
        for(int i = 0;i < n && obstacleGrid[0][i] == 0;i++){//初始化第一行
            dp[i] = 1;
        }

        //遍历顺序
        for(int i = 1;i < m;i++){//外层是行
            for(int j = 0;j < n;j++){//内层是列
                if(j > 0 && obstacleGrid[i][j] == 0){
                    dp[j] += dp[j-1];
                }else if(obstacleGrid[i][j] == 1){
                    dp[j]= 0;
                }
            }
        }
        return dp[n-1];
    }
};

总结

对比 记录 七十四【62.不同路径】
在这里插入图片描述
(欢迎指正,转载标明出处)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值