刷题记录 动态规划-7: 63. 不同路径 II

题目:63. 不同路径 II

难度:中等

给定一个 m x n 的整数数组 grid。一个机器人初始位于 左上角(即 grid[0][0])。机器人尝试移动到 右下角(即 grid[m - 1][n - 1])。机器人每次只能向下或者向右移动一步。

网格中的障碍物和空位置分别用 1 和 0 来表示。机器人的移动路径中不能包含 任何 有障碍物的方格。

返回机器人能够到达右下角的不同路径数量。

测试用例保证答案小于等于 2 * 109

示例 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

一、模式识别

类似于刷题记录 动态规划-5: 62. 不同路径-CSDN博客,本题我尝试过三种解法:

首先最容易想到的就是基于递归的DFS(深度优先搜索),

然后如果沿着递推公式能想到从终点返回起点这一层就能写出动态规划解法

最后代码随想录还给出了算组合数的方法(数论)

1.DFS

同样地,根据动态规划方法的递推公式可以直接写出基于递归的DFS

2.动态规划

本题属于经典动态规划问题,而且动规的很多要素题干中已经直接给出

五部曲:

1.动规数组意义:位于位置(i, j)时剩余的总步数

2.递推公式:dp(i, j) = dp(i - 1, j) + dp(i, j - 1)

解释:

机器人处于位置(i, j)时,每次只能向下或者向右移动一步两种选择,

分别可以到达(i - 1, j)和位置(i, j - 1),

由于障碍物的存在,会有两类边界情况:

①遇见障碍物dp(i, j) = 0

②当机器人走到边缘位置(i == m - 1 or j == n - 1),路径便只剩下一条:

边缘位置没有障碍物时,路径只剩下一条, dp(i, j) = 1

只要边缘位置有至少一个障碍物,该路径就会被堵死,

不仅时障碍物位置,整条边缘线都被堵死:dp(i, j) = 0

3.初始化:题干:一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )

4.遍历顺序:本题常规,根据递推公式:

注意,dp的访问顺序和机器人的寻路顺序完全相反

5.举例:略

3.数论:算组合数

对于无障碍物的情况:

无论怎么走,从起点(m, n)到终点(1, 1)都要走m + n - 2步,

其中,横向m - 1步,纵向n - 1步

因此该问题就顺理成章的转化成了C(m + n - 2), (m - 1)的组合问题

如果有障碍物,我一开始的思路是

有障碍物总数:总数 - 起点到障碍物路数×障碍物到终点路数

但这个思路是不行的!!!我会在后面详述

二、代码实现

1.DFS

这方法很好想,而且代码简洁,

思路是顺着机器人寻路,所以可读性强,只需要把递推公式表达清楚即可

但就是有个小小的问题:会超时!

class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        m, n = len(obstacleGrid), len(obstacleGrid[0])
        def helper(i, j):
            if obstacleGrid[i][j] == 1:
                return 0
            if i == m - 1 and j == n - 1:
                return 1
            if i == m - 1:
                return helper(i, j + 1)
            if j == n - 1:
                return helper(i + 1, j)
            return helper(i + 1, j) + helper(i, j + 1)
        return helper(0, 0)

问题源于其指数级的时间复杂度:O(2^(m + n - 1) - 1)

2.动态规划

注意和代码随想录不一样,

我自己写的时候二维OMN空间写法时直接用obstacleGrid充当dp数组,

所以遍历顺序反序,返回值位置是(0, 0)

(1)二维OMN空间写法

class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        m, n = len(obstacleGrid), len(obstacleGrid[0])
        for i in range(m - 1, -1, -1):
            for j in range(n - 1, -1, -1):
                if obstacleGrid[i][j] == 1:
                    obstacleGrid[i][j] = 0
                else:
                    if i == m - 1:
                        if j < n - 1 and obstacleGrid[i][j + 1] == 0:
                            obstacleGrid[i][j] = 0
                        else:
                            obstacleGrid[i][j] = 1
                    elif j == n - 1:
                        if i < m - 1 and obstacleGrid[i + 1][j] == 0:
                            obstacleGrid[i][j] = 0
                        else:
                            obstacleGrid[i][j] = 1
                    else:
                        obstacleGrid[i][j] = obstacleGrid[i + 1][j] + obstacleGrid[i][j + 1]
        return obstacleGrid[0][0]
  • 时间复杂度:O(m × n)
  • 空间复杂度:O(m × n)

耗时:0ms

(2)一维ON空间写法

class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        m, n = len(obstacleGrid), len(obstacleGrid[0])
        dp = [1] * n
        for i in range(m - 1, -1, -1):
            for j in range(n - 1, -1, -1):
                if obstacleGrid[i][j] == 1:
                    dp[j] = 0
                else:
                    if i == m - 1:
                        if j < n - 1 and dp[j + 1] == 0:
                            dp[j] = 0
                        else:
                            dp[j] = 1
                    elif j == n - 1:
                        if i < m - 1 and dp[j] == 0:
                            dp[j] = 0
                        else:
                            dp[j] = 1
                    else:
                        dp[j] += dp[j + 1]
        return dp[0]
  • 时间复杂度:O(m × n)
  • 空间复杂度:O(n)

耗时:0ms

可读性有点差,所以稍微解释一下,和二维空间代码的对应关系:

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

dp[i][j]: 更新后的dp[j]

dp[i - 1][j]: 更新前的dp[j]

dp[i][j - 1]: dp[j - 1]

三、为什么本题不能用算组合数的方法做?

1.欠考虑的情况

如果按照“总数 - 起点到障碍物路数×障碍物到终点路数”的思路,将写出以下代码:

class Solution:
    def calculate_conbination(self, m, n):
        num = 1
        a, b = m + n - 2, min(m - 1, n - 1)
        count = b
        while count > 0:
            num *= a
            a -= 1
            while b > 0 and num % b == 0:
                num //= b
                b -= 1
            count -= 1
        return num

    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        m, n = len(obstacleGrid), len(obstacleGrid[0])
        ob = False
        x, y = 0, 0
        for i in range(m):
            for j in range(n):
                if obstacleGrid[i][j] == 1:
                    x, y = i, j
                    ob = True
        return self.calculate_conbination(m, n) - (self.calculate_conbination(x + 1, y + 1) * self.calculate_conbination(m - x, n - y)) if ob else self.calculate_conbination(m, n)

然后就会这样:

输入

obstacleGrid =

[[0,1],[1,0]]

输出

1

预期结果

0

因为障碍物可能不止一个!!!

2.改进后发现该方法只能解决某些特殊情况

那继续改进,将公式改进为:总数 - Σ起点到障碍物路数×障碍物到终点路数

然后写出这个代码:

class Solution:
    def calculate_conbination(self, m, n):
        num = 1
        a, b = m + n - 2, min(m - 1, n - 1)
        count = b
        while count > 0:
            num *= a
            a -= 1
            while b > 0 and num % b == 0:
                num //= b
                b -= 1
            count -= 1
        return num

    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        m, n = len(obstacleGrid), len(obstacleGrid[0])
        obs = []
        for i in range(m):
            for j in range(n):
                if obstacleGrid[i][j] == 1:
                    obs.append((i, j))
        ans = self.calculate_conbination(m, n)
        for x, y in obs:
            ans -= (self.calculate_conbination(x + 1, y + 1) * self.calculate_conbination(m - x, n - y))
        return ans

 然后就会这样:

输入

obstacleGrid =

[[0,0,0,0],[0,1,0,0],[0,0,0,0],[0,0,1,0],[0,0,0,0]]

输出

0

预期结果

7

为什么呢?

因为你当障碍物数量超过一个,可能有些路径被重复计算!

所以该方法只适用于障碍物数量少于等一个的情况!!!

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/964817.html

    如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

    相关文章

    springboot+vue+uniapp的校园二手交易小程序

    开发语言&#xff1a;Java框架&#xff1a;springbootuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#…

    Python 自学秘籍:开启编程之旅,人生苦短,我用python。

    从2009年&#xff0c;用了几次python后就放弃了&#xff0c;一直用的php&#xff0c;现在人工智能时代&#xff0c;完全没php什么事情。必须搞python了&#xff0c;虽然已经40多岁了。死磕python了。让滔滔陪着你一起学python 吧。 开启新世界 在当今人工智能化的时代&#xff…

    react的antd表格自定义图标

    将原版的加号换成箭头 自定义图标 安装图标包&#xff1a; npm install --save ant-design/icons 引入&#xff1a; import { RightOutlined, DownOutlined } from ant-design/icons; 参数是一个函数 <Table columns{columns} dataSource{data} indentSize{20}expandIc…

    chrome浏览器chromedriver下载

    chromedriver 下载地址 https://googlechromelabs.github.io/chrome-for-testing/ 上面的链接有和当前发布的chrome浏览器版本相近的chromedriver 实际使用感受 chrome浏览器会自动更新&#xff0c;可以去下载最新的chromedriver使用&#xff0c;自动化中使用新的chromedr…

    Vim的基础命令

    移动光标 H(左) J(上) K(下) L(右) $ 表示移动到光标所在行的行尾&#xff0c; ^ 表示移动到光标所在行的行首的第一个非空白字符。 0 表示移动到光标所在行的行首。 W 光标向前跳转一个单词 w光标向前跳转一个单词 B光标向后跳转一个单词 b光标向后跳转一个单词 G 移动光标到…

    算法与数据结构(括号匹配问题)

    思路 从题干可以看出&#xff0c;只要给出的括号对应关系正确&#xff0c;那么就可以返回true,否则返回false。这个题可以使用栈来解决 解题过程 首先从第一个字符开始遍历&#xff0c;如果是括号的左边&#xff08;‘&#xff08;‘&#xff0c;’[‘&#xff0c;’}‘&…

    deepseek、qwen等多种模型本地化部署

    想要在本地部署deepseek、qwen等模型其实很简单,快跟着小编一起部署吧 1 环境搭建 1.1下载安装环境 首先我们需要搭建一个环境ollama,下载地址如下 :Ollama 点击Download 根据自己电脑的系统选择对应版本下载即可 1.2 安装环境(window为例) 可以直接点击安装包进行安…

    (2025,LLM,下一 token 预测,扩散微调,L2D,推理增强,可扩展计算)从大语言模型到扩散微调

    Large Language Models to Diffusion Finetuning 目录 1. 概述 2. 研究背景 3. 方法 3.1 用于 LM 微调的高斯扩散 3.2 架构 4. 主要实验结果 5. 结论 1. 概述 本文提出了一种新的微调方法——LM to Diffusion (L2D)&#xff0c;旨在赋予预训练的大语言模型&#xff08;…

    一款wordpress AI免费插件自动内容生成+前端AI交互+文章批量采集

    一款wordpressAI自动内容生成前端AI会话窗口交互文章批量采集免费插件 1. SEO优化文章生成 关键词驱动的内容生成&#xff1a;用户可以输入关键词或长尾关键词&#xff0c;插件会根据这些关键词生成高质量的SEO优化文章。文章结构清晰&#xff0c;语言自然流畅&#xff0c;符合…

    2024年12月 Scratch 图形化(一级)真题解析 中国电子学会全国青少年软件编程等级考试

    202412 Scratch 图形化&#xff08;一级&#xff09;真题解析 中国电子学会全国青少年软件编程等级考试 一、单选题(共25题&#xff0c;共50分) 第 1 题 点击下列哪个按钮&#xff0c;可以将红框处的程序放大&#xff1f;&#xff08; &#xff09; A. B. C. D. 标…

    Java面试题集合篇5:10道基础面试题——保姆级详细图文、代码解释

    文章目录 前言41、多线程场景下使用 ArrayList42、List 和 Set 区别43、HashSet 实现原理44、HashSet检查重复和保证数据不可重复45、BlockingQueue46、Map接口46.1、HashMap 实现原理46.2、HashMap在JDK1.7和JDK1.8中不同点46.3、JDK1.7 VS JDK1.8 比较 47、HashMap的put方法流…

    2022年全国职业院校技能大赛网络系统管理赛项模块A:网络构建(样题2)-网络部分解析-附详细代码

    目录 附录1:拓扑图​编辑 附录2:地址规划表 1.SW1 2.SW2 3.SW3 4.SW4 5.SW5 6.SW6 7.SW7 8.R1 9.R2 10.R3 11.AC1 12.AC2 13.EG1 14.EG2 15.AP2 16.AP3 附录1:拓扑图 附录2:地址规划表

    优化数据库结构

    MySQL学习大纲 一个好的数据库设计方案对于数据库的性能尝尝会起到事倍功半的效果&#xff0c;合理的数据库结构不仅使数据库占用更小的磁盘空间&#xff0c;而且使查询速度更快。数据库结构的设计需要考虑数据冗余、查询和更新速度、字段的数据类型是否合理等多方面的内容&…

    【deepseek实战】绿色好用,不断网

    前言 最佳deepseek火热网络&#xff0c;我也开发一款windows的电脑端&#xff0c;接入了deepseek&#xff0c;基本是复刻了网页端&#xff0c;还加入一些特色功能。 助力国内AI&#xff0c;发出自己的热量 说一下开发过程和内容的使用吧。 目录 一、介绍 二、具体工作 1.1、引…

    Meta Sapiens AI论文解读:人类视觉模型基石初现,AI 未来走向何方?

    一、引言 在本文中&#xff0c;我们将深入探讨 Meta AI 的一项新成果&#xff0c;该成果发表于一篇题为《Sapiens&#xff1a;人类视觉模型的基础》的研究论文中。这篇论文介绍了一系列模型&#xff0c;这些模型针对四项以人类为中心的基本任务&#xff0c;正如我们在上面的演示…

    多用户同时RDP登入Win10

    自备以下文件&#xff1a; winsw_frpc.xml展开如下&#xff1a; <service><!-- ID of the service. It should be unique across the Windows system--><id>winsw_frp</id><!-- Display name of the service --><name>winsw_frp</na…

    2025 持续防范 GitHub 投毒,通过 Sharp4SuoExplorer 分析 Visual Studio 隐藏文件

    在2024年底的网络安全事件中&#xff0c;某提权工具被发现植入后门&#xff0c;攻击者利用 .suo 文件作为隐蔽的攻击方式。由于 .suo 文件是 Visual Studio 项目的隐藏配置文件&#xff0c;通常不为安全研究人员所关注&#xff0c;因此为攻击者提供了潜在的攻击渠道。 初步调查…

    Unity 2D实战小游戏开发跳跳鸟 - 记录显示最高分

    上一篇文章中我们实现了游戏的开始界面,在开始界面中有一个最高分数的UI,本文将接着实现记录最高分数以及在开始界面中显示最高分数的功能。 添加跳跳鸟死亡事件 要记录最高分,则需要在跳跳鸟死亡时去进行判断当前的分数是否是最高分,如果是最高分则进行记录,如果低于之前…

    牛客 - 链表相加(二)

    描述 假设链表中每一个节点的值都在 0 - 9 之间&#xff0c;那么链表整体就可以代表一个整数。 给定两个这种链表&#xff0c;请生成代表两个整数相加值的结果链表。 数据范围&#xff1a;0≤n,m≤1000000&#xff0c;链表任意值 0≤val≤9 要求&#xff1a;空间复杂度 O(n)&am…

    Math Reference Notes: 符号函数

    1. 符号函数的定义 符号函数&#xff08;Sign Function&#xff09; sgn ( x ) \text{sgn}(x) sgn(x) 是一个将实数 ( x ) 映射为其 符号值&#xff08;即正数、负数或零&#xff09;的函数。 它的定义如下&#xff1a; sgn ( x ) { 1 如果 x > 0 0 如果 x 0 − 1 如…