问题描述

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 1 和 0 来表示。

说明:mn 的值均不超过 100。

示例 1:

输入:

[

[0,0,0],

[0,1,0],

[0,0,0]

]

输出: 2

解释:

3x3 网格的正中间有一个障碍物。

从左上角到右下角一共有 2 条不同的路径:

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

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

动态规划解决

前面我们讲过的409,动态规划求不同路径和这非常类似的一道题,只不过上一篇没有障碍物,但并不影响我们解这道题,我们还用dp[i][j]表示到坐标(i,j)这个格内有多少条不同的路径,所以最终的答案就是求dp[m-1][n-1]。

这里的递推分为两种情况,一种是当前网格没有障碍物,一种是当前网格有障碍物。

1,如果当前网格dp[i][j]有障碍物,那么这里肯定是走不过去的,所以dp[i][j]=0。

2,如果当前网格dp[i][j]没有障碍物,那么递推公式就和上一题409,动态规划求不同路径一样了。

因为只能从上面或左边走过来,所以递推公式是

dp[i][j]=dp[i-1][j]+dp[i][j-1]。

  • dp[i-1][j]表示的是从上面走过来的路径条数。
  • dp[i][j-1]表示的是从左边走过来的路径条数。

边界条件也好判断,如果当前行没有障碍物,那么当前行的值都是1,如果有障碍物,那么第一个障碍物前面都是1,其他的都是0。同理第一列也一样。

我们来看下代码

public int uniquePathsWithObstacles(int[][] obstacleGrid) {    int m = obstacleGrid.length;    int n = obstacleGrid[0].length;    int dp[][] = new int[m][n];    //第一列初始化    for (int i = 0; i 

代码和409,动态规划求不同路径差不多,只不过在第一行和第一列还有上面第21行多了一些判断。

动态规划代码量优化

上面代码虽然也能实现,但有那么多条件判断总感觉很繁琐,所以我们还有一种方式就是把二维数组的长和宽都放大一格,这样数组的第一行和第一列都不存储任何值,但初始条件要变了

  • dp[1][1] = obstacleGrid[0][0] ^ 1;
  • dp[0][1] = 1;
  • dp[1][0] = 1;

上面3种初始条件都可以,我们来任选一个,看下代码

public int uniquePathsWithObstacles(int[][] obstacleGrid) {    int m = obstacleGrid.length;    int n = obstacleGrid[0].length;    int dp[][] = new int[m + 1][n + 1];    //初始条件,下面3个任选一个    //dp[1][1] = obstacleGrid[0][0] ^ 1;    //dp[0][1] = 1;    dp[1][0] = 1;    for (int i = 1; i <= m; ++i)        for (int j = 1; j <= n; ++j)            if (obstacleGrid[i - 1][j - 1] == 0)                dp[i][j] += dp[i - 1][j] + dp[i][j - 1];    return dp[m][n];}

动态规划空间优化

我们可以参照上一题把二维空间改为一维的,原理很简单,我们来直接看代码

public int uniquePathsWithObstacles(int[][] obstacleGrid) {    int m = obstacleGrid.length;    int n = obstacleGrid[0].length;    int[] dp = new int[n + 1];    dp[1] = 1;    for (int i = 0; i 

上一题有人问过一个问题说看不懂第11行,这里再说一下,因为是一行一行的遍历,在当前行遍历之前dp(这里是一维数组)表示的是上一行的值,然后遍历到当前行的时候,假如遍历当前行的第j列的时候,那么当前行第j列之前的数据都会被更新掉,当前行第j列之后的数据还是上一行的,所以dp[j]=dp[j]+dp[j-1](为了区分,这里标成了不同的颜色),dp[j]表示的是当前列的上一行值,dp[j-1]表示的是当前行的前一个值。

递归方式

上一题我们提到过,使用递归的方式会造成大量的重复计算,所以为了减少重复计算,这里使用一个map把计算过的值存储起来,下次用的时候先从map中取,如果有就返回,如果没有再计算。

public int uniquePathsWithObstacles(int[][] obstacleGrid) {    return helper(obstacleGrid, 0, 0, new HashMap());}public static int helper(int[][] obstacleGrid, int down, int right, Map map) {    String key = down + "and" + right;    int result = 0;    if (map.containsKey(key))        return map.get(key);    if (obstacleGrid[down][right] == 1) {        result = 0;        map.put(key, result);        return result;    }    if (right == obstacleGrid[0].length - 1 && down == obstacleGrid.length - 1) {        if (obstacleGrid[down][right] == 1) {            result = 0;        } else {            result = 1;       }        map.put(key, result);        return result;    }    if (right == obstacleGrid[0].length - 1 || down == obstacleGrid.length - 1) {        if (right == obstacleGrid[0].length - 1) {            result = helper(obstacleGrid, down + 1, right, map);        } else {            result = helper(obstacleGrid, down, right + 1, map);        }        map.put(key, result);        return result;    }    result = helper(obstacleGrid, down, right + 1, map) + helper(obstacleGrid, down + 1, right, map);    map.put(key, result);    return result;}

这种不看也可以,因为动态规划非常简单,没人会傻到会使用这种方式,但他也算是提供了一种思路,有时间看看也行。

总结

这题多了一个障碍物的判断,但难度其实并没有增加多少,如果当前位置出现了障碍物,说明不能从当前位置通过,所以当前位置的路径是0,如果当前位置不是0,那么计算就还和以前一样了。

如果喜欢这篇文章还可以关注微信公众号“数据结构和算法”,查看更多的算法题

动态规划立体匹配代码_411,动态规划和递归求不同路径 II相关推荐

  1. python用动态规划求删除路径_Python | 动态规划求解TSP

    解题思路主要有两部分: i为当前节点(城市),S为还没有遍历的节点(城市集合),表示从第i个节点起,经历S集合中所有的点,到达终点的最短路径长度. 回溯找到最优的路径,需要将S集合一一对应一个数字(类 ...

  2. 动态规划走楼梯_动态规划问题为什么要画表格?

    ❝ 本文是我的 91 算法第一期的部分讲义内容.91 算法第一期已经接近尾声,二期的具体时间关注我的公众号即可,一旦开放,会第一时间在公众号<力扣加加>通知大家. ❞ 动态规划可以理解为是 ...

  3. 63. 不同路径 II【动态规划】

    63.不同路径 II 题目链接:https://leetcode-cn.com/problems/unique-paths-ii/ 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 ...

  4. 动态规划生产存储matlab,动态规划方法的matlab实现及其应用

    <动态规划方法的matlab实现及其应用>由会员分享,可在线阅读,更多相关<动态规划方法的matlab实现及其应用(6页珍藏版)>请在人人文库网上搜索. 1.动态规划方法的动态 ...

  5. python 动态规划 旅行商问题_旅行商问题动态规划解法(python版)

    2019年华为实习生第二场笔试第二题是个旅行商问题,虽然只有5个点可以import itertools产生排列遍历5!=120情况求解(当然也可以写个递归自己生成排列),还是查了下动态规划的解法. 原 ...

  6. 递归求嵌套数组中最大值

    今早在 https://attachments.me/hirehack/public/computer.html 做题,有一题是 递归求嵌套数组中最大值: /* This challenge requ ...

  7. 递归求最值(分治递归)

    输入n,再输入n个数,输出n个数中的最小值和最大值. 分治与递归! 代码: #include<iostream>using namespace std; void minmax(int * ...

  8. C语言用递归求斐波那契数,让你发现递归的缺陷和效率瓶颈

    C语言用递归求斐波那契数,让你发现递归的缺陷和效率瓶颈 分享到: QQ空间 新浪微博 腾讯微博 豆瓣 人人网 递归是一种强有力的技巧,但和其他技巧一样,它也可能被误用. 一般需要递归解决的问题有两个特 ...

  9. Python函数详解:函数定义、调用,lambda函数,高阶函数map,filter,reduce,函数式编程,模块化设计、代码复用、函数递归、enumerate()

    一.函数 函数是一段具有特定功能的.可重用的语句组,通过函数名来表示和调用. 函数是一段代码的抽象和封装 函数是一段具有特定功能的.可重用的语句组 函数是一种功能的抽象,表达特定功能 两个作用:降低编 ...

最新文章

  1. 玩点创意编程,发现另一个世界
  2. springboot的war和jar包
  3. 计算机解译地学应用效果,不同的遥感解译方法在地表覆盖分类信息提取中的应用研究...
  4. Redis Scan 命令
  5. hibernate框架学习笔记11:Criteria查询详解
  6. Away3D学习笔记-物体位置和移动
  7. 初识Loadrunner
  8. 树莓派 opencv 调用摄像头
  9. PicSizer-将图片压缩到指定大小(KB)的软件
  10. python时间序列分析——基于混沌和数据分形理论的特征构建
  11. 图解css3:核心技术与案例实战. 导读
  12. 助力CP扬帆出海,华为HiGame邀全国开发者共赢拉美市场
  13. size不变的图片,用于目标检测
  14. 月份 星期 英文描述
  15. Android 状态栏适配
  16. Software Protection无法启动造成slmgr.vbs激活win10失败
  17. node内存溢出问题 Javascript Heap out of memory
  18. 还在纠结配色问题?手把手教你用MATLAB一键生成高质量色卡
  19. 如何下载virtualbox的旧版本
  20. Chrome浏览器87端口限制问题

热门文章

  1. FineReport中以jws方式调用WebService数据源方案
  2. Strut2判断是否是AJAX调用
  3. MFC提供的集合类CStringArray类和CPtrArray类
  4. 关于ESP8266 GPIO中断使用的总结
  5. 内核request_mem_region 和 ioremap的理解
  6. python 语言教程(2)基础语法之标识符
  7. 《研磨设计模式》chap24 桥接模式bridge(2)场景应用
  8. 比特币钱包(2) BIP32 HD钱包之生成子密钥
  9. 以太坊节点布置(3) 启动geth客户端
  10. 区块链BaaS云服务(11)招商银行ABS区块链平台