【Leetcode】【DP-二维数组】 63. Unique Paths II / 不同路径2(带障碍)

给定一个二维数组,每格为0/1值,1代表无法通过。求从左上到右下的不同路径数。只能往右/下走。

Input:
[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]
Output: 2
Explanation:
There is one obstacle in the middle of the 3x3 grid above.
There are two ways to reach the bottom-right corner:
1. Right -> Right -> Down -> Down
2. Down -> Down -> Right -> Right


该题较62题多了个障碍的概念。方法相似,只要将障碍处的dp设为0即可(带障碍说明其后续的路径必然不会经过这里),依然可知dp式子:dp [i] [j] = dp [i-1] [j] + dp [i] [j-1],但是需要考虑几种情况:


情况(1),在第一行或者第一列中出现了障碍。此时易知,第一行自障碍开始到后面,路径数都为0(因为此路不通,见(1)的第一行)。第一列同理。

情况(2)(3)则是起点/终点本身无法通过,结果为0。

基于上面的情况,两步走:

(1)初始值的设定:第一行与第一列,若不为障碍,则设为1,若为障碍,则设为0

先将整个路径数矩阵dp置为0,然后对第一行/列进行初始化。若不为障碍,则设为1,若为障碍,则设为0,且跳出循环,这样后面的几个dp值即为初始值0。

        vector<vector<int>> dp (m, vector<int>(n, 0));  // 构造二维数组存储每一处的dp值
        for(int i=0; i<m; i++) {                // 为第一列初始化
            if(obstacleGrid[i][0] == 1) {       // 若当前格值为1,则dp值为0,并且之后的值为初始值0
                    // dp[i][0] = 0;        // 初始值即为0,此行可注释掉
                    break;                // 跳出后,此后的元素则为初始值0
                }
            dp[i][0] = 1;
        }
        for(int i=0; i<n; i++) {            // 第一行,同第一列
            if(obstacleGrid[0][i] == 1) { 
                    // dp[0][i] = 0;
                    break;
                }
            dp[0][i] = 1;
        }

(2)dp公式:dp [i] [j] = dp [i-1] [j] + dp [i] [j-1],进行求解。

        for(int i=1; i<m; i++) {            
            for(int j=1; j<n; j++) {
                if(obstacleGrid[i][j] == 1) {       // 若当前格值为1,则dp值为0
                    dp[i][j] = 0;
                    continue;
                }
                dp[i][j] = dp[i-1][j] + dp[i][j-1];   // 每一处与左侧即上侧相关。
            }
        }

(3)返回最后一个即为到达右下角的路径数。

总代码:

    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        if(obstacleGrid.size() == 0)
            return 0;
        int m = obstacleGrid.size();    // 行数
        int n = obstacleGrid[0].size(); // 列数
        if(obstacleGrid[0][0] == 1 || obstacleGrid[m-1][n-1] == 1)  // 起点/终点为1,无法通过,则路径数为0
            return 0;
        vector<vector<int>> dp (m, vector<int>(n, 0));  // 构造二维数组存储每一处的dp值
        for(int i=0; i<m; i++) {                // 为第一列初始化
            if(obstacleGrid[i][0] == 1) {       // 若当前格值为1,则dp值为0,并且之后的值为初始值0
                    // dp[i][0] = 0;        // 初始值即为0,此行可注释掉
                    break;
                }
            dp[i][0] = 1;
        }
        for(int i=0; i<n; i++) {            // 第一行,同第一列
            if(obstacleGrid[0][i] == 1) { 
                    // dp[0][i] = 0;
                    break;
                }
            dp[0][i] = 1;
        }

        
        for(int i=1; i<m; i++) {            
            for(int j=1; j<n; j++) {
                if(obstacleGrid[i][j] == 1) {       // 若当前格值为1,则dp值为0
                    dp[i][j] = 0;
                    continue;
                }
                dp[i][j] = dp[i-1][j] + dp[i][j-1];   // 每一处与左侧即上侧相关。
            }
        }
        return dp[m-1][n-1];
    }


优化内存使用空间,使用一维数组即可。

(1)设置初始值:先为第一行进行初始化,若为障碍,则dp值为0,且后续都为0。

(2)DP公式:dp[j] = dp[j-1] + dp[j]。行从第一行开始,列元素从第0个开始。所以要考虑第一列上,若上一个元素为0时的情况。

总代码:

    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        if(obstacleGrid.size() == 0)
            return 0;
        int m = obstacleGrid.size();    // 行数
        int n = obstacleGrid[0].size(); // 列数
        if(obstacleGrid[0][0] == 1 || obstacleGrid[m-1][n-1] == 1)  // 起点/终点为1,无法通过,则路径数为0
            return 0;
        vector<int> dp (n, 0);               // 构造一维数组存储dp值
        for(int i=0; i<n; i++) {                // 为第一行初始化
            if(obstacleGrid[0][i] == 1) {       // 若当前格值为1,则dp值为0,并且之后的值为初始值0
                    // dp[i][0] = 0;        // 初始值即为0,此行可注释掉
                    break;
                }
            dp[i] = 1;
        }

        for(int i=1; i<m; i++) {            
            for(int j=0; j<n; j++) {
                if(obstacleGrid[i][j] == 1) {       // 若当前格值为1,则dp值为0
                    dp[j] = 0;
                    continue;
                }
                if(j == 0) {        // 对于第一列的元素
                    if(dp[j] == 0)  // 若上侧为0,则当前也为0
                        continue;
                    dp[j] = 1;
                }
                dp[j] = dp[j-1] + dp[j];   // 每一处与左侧即上侧相关。
            }
        }
        return dp[n-1];
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值