leetcode63. 不同路径 II(动态规划-java)

不同路径 II

  • leetcode63. 不同路径 II
    • 题目描述
    • 暴力递归
    • 代码演示
    • 动态规划
      • 代码演示
    • 动态规划空间压缩
  • 动态规划专题

leetcode63. 不同路径 II

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/unique-paths-ii

题目描述

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。

示例1:
leetcode63. 不同路径 II(动态规划-java)_第1张图片
输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:

  1. 向右 -> 向右 -> 向下 -> 向下
  2. 向下 -> 向下 -> 向右 -> 向右

示例2:
leetcode63. 不同路径 II(动态规划-java)_第2张图片
输入:obstacleGrid = [[0,1],[0,0]]
输出:1

提示:
m == obstacleGrid.length
n == obstacleGrid[i].length
1 <= m, n <= 100
obstacleGrid[i][j] 为 0 或 1

暴力递归

这题是leetcode62. 不同路径 的拓展版.加了障碍物,解题思路是一样的,只是要加下障碍物的判断.
还是向下和向右两个方向的选择,两种情况的和就是所有的路线数.

代码演示

/**
* 主方法
*/
 public int uniquePathsWithObstacles(int[][] obstacleGrid) {
 	//如果右下角是障碍物,怎么都过不去,直接返回0
        if(obstacleGrid[obstacleGrid.length - 1][obstacleGrid[0].length - 1] == 1){
            return 0;
        }
        //开始递归
        return process(obstacleGrid,0,0);
    }
	/**
	* 暴力递归
	* i 和 j 描述当前在的位置
	*/
    public int process(int[][]obstacleGrid,int i,int j){
    	//base case 来到最后一个位置,前面路线有效,返回1
        if(i == obstacleGrid.length - 1 && j == obstacleGrid[0].length - 1 ){
            return 1;
        }
        //越界,路线无效 返回0
        if(i >= obstacleGrid.length || j >= obstacleGrid[0].length){
            return 0;
        }
        // 碰到障碍物,无效返回0
        if(obstacleGrid[i][j] == 1){
            return 0;
        }
        //x向下和向右两种情况
        int down = process(obstacleGrid,i + 1,j);
        int right = process(obstacleGrid,i,j + 1);
        //两种情况累加就是所有的路线
        return down + right;
    }

动态规划

从暴力递归中可以得知,(i,j) 位置依赖 (i+1,j)和(i,j+1)两个位置,所以可以轻松得到状态转移方程是:

f(i,j) = f(i+1,j) + f(i,j+1;
但这里有一些需要注意的地方,就是障碍物的处理,和dp表的初始化,我们以图为例:
leetcode63. 不同路径 II(动态规划-java)_第3张图片
上面图代表要走的网格,我只填写了最后一行和一列其他位置没有标注,因为现在只讨论最后一行和一列的情况.
最后一行为例,三角符号标注的位置是最后一次出现的障碍物,在这个障碍物之前的最后一行位置,都无法到最后位置了,所以之前的位置在dp 表中要初始化为0,之后的可以初始化为1,
最后一列也是同样情况:
下面看下代码中如何处理;

代码演示

  /**
     * 动态规划
     * @param obstacleGrid
     * @return
     */
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int n = obstacleGrid.length;
        int m = obstacleGrid[0].length;
        //最有下角如果是障碍物,就无法过去,返回 0
        if (obstacleGrid[n - 1][m - 1] == 1) {
            return 0;
        }
        //动态规划表
        int[][]dp = new int[n][m];
        //来标记最后一行最后一次出现障碍物的位置
        int flagN = -1;
        //来标记最后一列最后一次出现障碍物的位置
        int flagM = -1;
        for (int i = m - 1; i >= 0;i--){
            if(obstacleGrid[n - 1][i] == 1){
                flagN = i;
                break;
            }
        }
        //最后一个障碍物之后的位置初始化为1
        for (int i = flagN + 1; i < m;i++){
            dp[n - 1][i] = 1;
        }
        //标记最后一列最后一次出现障碍物的位置.
        for (int j =  n - 1; j >= 0;j--){
            if(obstacleGrid[j][m - 1] == 1){
                flagM = j;
                break;
            }
        }
        //最后一个障碍物之后的位置初始化为1
        for (int j = flagM + 1; j < n;j++){
            dp[j][m - 1] = 1;
        }

        for (int i = n - 2;i >= 0;i--){
            for (int j = m - 2;j >= 0;j--){
                //当前位置本身是障碍物 即为0
                if(obstacleGrid[i][j] == 1){
                    dp[i][j] = 0;
                }else{
                    //z状态转移方程
                    dp[i][j] = dp[i + 1][j] + dp[i][j + 1];
                }

            }
        }
        return dp[0][0];
    }

动态规划空间压缩

  /**
     * 动态规划 + 空间压缩
     * @param obstacleGrid
     * @return
     */
    public int uniquePathsWithObstacles2(int[][] obstacleGrid) {
        int n = obstacleGrid.length;
        int m = obstacleGrid[0].length;
        if (obstacleGrid[n - 1][m - 1] == 1) {
            return 0;
        }
        int[]dp = new int[m];
        int flagN = -1;
        for (int i = m - 1; i >= 0;i--){
            if(obstacleGrid[n - 1][i] == 1){
                flagN = i;
                break;
            }
        }
        for (int i = flagN + 1; i < m;i++){
            dp[i] = 1;
        }
        int flagM = -1;
        for (int j =  n - 1; j >= 0;j--){
            if(obstacleGrid[j][m - 1] == 1){
                flagM = j;
                break;
            }
        }

        for (int i = n - 2;i >= 0;i--){
            dp[m - 1] = i > flagM ? 1 : 0;
            for (int j = m - 2;j >= 0;j--){
                if(obstacleGrid[i][j] == 1){
                    dp[j] = 0;
                }else{
                    dp[j] = dp[j] + dp[j + 1];
                }

            }
        }
        return dp[0];
    }

动态规划专题

leetcode62. 不同路径

leetcode877. 石子游戏

leetcode64. 最小路径和

leetcode416. 分割等和子集

leetcode354. 俄罗斯套娃信封问题

leetcode300. 最长递增子序列

leetcode337. 打家劫舍 III

你可能感兴趣的:(算法,java,数据结构,动态规划,java,算法,leetcode,数据结构)