代码随想录算法训练营第三十九天丨 动态规划part02

news2025/2/27 20:45:07

62.不同路径

思路

动态规划

机器人从(0 , 0) 位置出发,到(m - 1, n - 1)终点。

按照动规五部曲来分析:

  • 确定dp数组(dp table)以及下标的含义

dp[i][j] :表示从(0 ,0)出发,到(i, j) 有dp[i][j]条不同的路径。

  • 确定递推公式

想要求dp[i][j],只能有两个方向来推导出来,即dp[i - 1][j] 和 dp[i][j - 1]。

此时在回顾一下 dp[i - 1][j] 表示啥,是从(0, 0)的位置到(i - 1, j)有几条路径,dp[i][j - 1]同理。

那么很自然,dp[i][j] = dp[i - 1][j] + dp[i][j - 1],因为dp[i][j]只有这两个方向过来。

  • dp数组的初始化

如何初始化呢,首先dp[i][0]一定都是1,因为从(0, 0)的位置到(i, 0)的路径只有一条,那么dp[0][j]也同理。

所以初始化代码为:

//初始化dp[][],将最上和最左初始化为1
for (int i = 0; i < m; i++) {
    dp[i][0] = 1;
    for (int j = 0; j < n; j++) {
        dp[0][j]=1;
    }
}
  • 确定遍历顺序

这里要看一下递推公式dp[i][j] = dp[i - 1][j] + dp[i][j - 1],dp[i][j]都是从其上方和左方推导而来,那么从左到右一层一层遍历就可以了。

这样就可以保证推导dp[i][j]的时候,dp[i - 1][j] 和 dp[i][j - 1]一定是有数值的。

  • 举例推导dp数组

如图所示:

62.不同路径1

以上动规五部曲分析完毕,代码如下:

class Solution {
    public int uniquePaths(int m, int n) {
        //确定dp数组
        int[][] dp = new int[m][n];
        //确定递推公式【状态转移方程】:dp[i][j] = dp[i-1][0] + dp[0][j-1]
        //初始化dp[][],将最上和最左初始化为1
        for (int i = 0; i < m; i++) {
            dp[i][0] = 1;
            for (int j = 0; j < n; j++) {
                dp[0][j]=1;
            }
        }
        //确定遍历顺序,从左到右
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[i][j] = dp[i-1][j] + dp[i][j-1];
            }
        }
        return dp[m-1][n-1];
    }
}
  • 时间复杂度:O(m × n)
  • 空间复杂度:O(m × n)

63. 不同路径 II

思路

这道题相对于62.不同路径 (opens new window)就是有了障碍。

62.不同路径 (opens new window)中已经详细分析了没有障碍的情况,有障碍的话,其实就是标记对应的dp table(dp数组)保持初始值(0)就可以了。

动规五部曲:

  • 确定dp数组(dp table)以及下标的含义

dp[i][j] :表示从(0 ,0)出发,到(i, j) 有dp[i][j]条不同的路径。

  • 确定递推公式

递推公式和62.不同路径一样,dp[i][j] = dp[i - 1][j] + dp[i][j - 1]。

但这里需要注意一点,因为有了障碍,(i, j)如果就是障碍的话应该就保持初始状态(初始状态为0)。

所以代码为:

int a = obstacleGrid[i-1][j] == 1 ? 0 : dp[i-1][j];
int b = obstacleGrid[i][j-1] == 1 ? 0 : dp[i][j-1];
dp[i][j] = a + b;
  • dp数组如何初始化

在62.不同路径 (opens new window)不同路径中我们给出如下的初始化:

for (int i = 0; i < m; i++) {
    dp[i][0] = 1;
    for (int j = 0; j < n; j++) {
        dp[0][j]=1;
    }
}

因为从(0, 0)的位置到(i, 0)的路径只有一条,所以dp[i][0]一定为1,dp[0][j]也同理。

但如果(i, 0) 这条边有了障碍之后,障碍之后(包括障碍)都是走不到的位置了,所以障碍之后的dp[i][0]应该还是初始值0。

如图:

63.不同路径II

下标(0, j)的初始化情况同理。

所以本题初始化代码为:

for (int i = 0; i < m; i++) {
    if (obstacleGrid[i][0] == 1){
        break;
    }
    dp[i][0] = 1;
    for (int j = 0; j < n; j++) {
        if (obstacleGrid[0][j] == 1){
            break;
        }
        dp[0][j]=1;
    }
}
  • 确定遍历顺序

从递归公式dp[i][j] = dp[i - 1][j] + dp[i][j - 1] 中可以看出,一定是从左到右一层一层遍历,这样保证推导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++) {
        int a = obstacleGrid[i-1][j] == 1 ? 0 : dp[i-1][j];
        int b = obstacleGrid[i][j-1] == 1 ? 0 : dp[i][j-1];
        dp[i][j] = a + b;
    }
}
  • 举例推导dp数组

拿示例1来举例如题:

63.不同路径II1

对应的dp table 如图:

63.不同路径II2

如果这个图看不懂,建议再理解一下递归公式,然后照着文章中说的遍历顺序,自己推导一下!

动规五部分分析完毕,代码如下:

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {

        //确定dp数组,及其下标含义
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        //当起始位置或者终点有障碍时,直接返回
        if (obstacleGrid[m-1][n-1] == 1 || obstacleGrid[0][0] == 1){
            return 0;
        }
        int[][] dp = new int[m][n];

        //确定递推公式
        //数组初始化,当遇到障碍直接跳出循环
        for (int i = 0; i < m; i++) {
            if (obstacleGrid[i][0] == 1){
                break;
            }
            dp[i][0] = 1;
            for (int j = 0; j < n; j++) {
                if (obstacleGrid[0][j] == 1){
                    break;
                }
                dp[0][j]=1;
            }
        }
        //确定遍历顺序
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                int a = obstacleGrid[i-1][j] == 1 ? 0 : dp[i-1][j];
                int b = obstacleGrid[i][j-1] == 1 ? 0 : dp[i][j-1];
                dp[i][j] = a + b;
            }
        }
        return dp[m-1][n-1];
    }
}
  • 时间复杂度:O(n × m),n、m 分别为obstacleGrid 长度和宽度
  • 空间复杂度:O(n × m)

今天感觉不错,再接再厉!!!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【每日一题】参加会议的最多员工数

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;内向基环树拓扑排序分类讨论内向基环树分类讨论基环长度大于 2基环长度等于 2 功能实现 写在最后 Tag 【内向基环树拓扑排序分类讨论】【图】【2023-11-01】 题目来源 2127. 参加会议的最多员工数 题目解读 员工只有…

阿昌教你如何优雅的数据脱敏

阿昌教你如何优雅的数据脱敏 Hi&#xff0c;我是阿昌&#xff0c;最近有一个数据脱敏的需求&#xff0c;要求用户可自定义配置数据权限&#xff0c;并对某种类型数据进行脱敏返回给前端 一、涉及知识点 SpringMVCJava反射Java自定义注解Java枚举 二、方案选择 1、需求要求…

呼叫中心的重要考核指标

呼叫中心在运营过程中越来越精细化&#xff0c;在信息化管理的时代&#xff0c;呼叫中心系统是必不可少的&#xff0c;而呼叫中心的管理人员为了提升运营效率&#xff0c;通常会根据业务目标设置各种业务的考核指标&#xff0c;而我也根据OKCC在呼叫中心项目运营过程中的经验&a…

【双十一预售】玩得越来越大了...

双十一又又又到了 剁手带来的快乐终究是短暂的 让自己变得更优秀才是长远的快乐 当今大环境 工作难找&#xff0c;钱难赚 只有不断学习与成长 方能应对未来的各种不确定性 知了堂双十一预售 0.11元畅享三大权益 助你快速实现自我提升 突破成长瓶颈 https://appyqk1x…

1. 网络之网络通信基础

网络通信基础 文章目录 网络通信基础1. IP地址2. 端口号3. 协议3.1 三要素3.2 作用 4. 五元组5. 协议分层5.1 OSI七层模型5.2 TCP/IP 五层模型5.2.1 应用层5.2.2 传输层5.2.3 网络层5.2.3 数据链路层5.2.5 物理层 6. 封装和分用6.1 发送方 - 封装6.2 中间转发6.3 接收方 - 分用…

codeMirror代码编辑器,如何定位并在编辑区域输入内容

背景 最近在写UI自动化&#xff0c;发现普通的方法不能在CodeMirror编辑器里面输入内容&#xff0c;只能通过JS的方式输入内容。 于是琢磨了一下selenium和playwright这2种自动化工具&#xff0c;在CodeMirror编辑器里面输入内容的差别。 注意&#xff1a;这里在定位CodeMirr…

轧钢厂安全生产方案:AI视频识别安全风险智能监管平台的设计

一、背景与需求 轧钢厂一般都使用打包机对线材进行打包作业&#xff0c;由于生产需要&#xff0c;人员需频繁进入打包机内作业&#xff0c;如&#xff1a;加护垫、整包、打包机检修、调试等作业。在轧钢厂生产过程中&#xff0c;每个班次生产线材超过300件&#xff0c;人员在一…

【OpenCV实现图像找到轮廓的不同特征,就像面积,周长,质心,边界框等等。】

文章目录 概要图像矩凸包边界矩形 概要 OpenCV是一个流行的计算机视觉库&#xff0c;它提供了许多图像处理和分析功能&#xff0c;其中包括查找图像中物体的轮廓。通过查找轮廓&#xff0c;可以提取许多有用的特征&#xff0c;如面积、周长、质心、边界框等。 以下是几种使用…

双目视觉检测 KX02-SY1000型测宽仪 有效修正和消除距离变化对测量的影响

双目视觉检测的基本原理 利用相机测量宽度时&#xff0c;由于单个相机在成像时存在“近大远小”的现象&#xff0c;并且单靠摄入的图像无法知道被测物的距离&#xff0c;所以由被测物的跳动导致的被测物到工业相机之间距离变化&#xff0c;使测量精度难以提高。 因此测宽仪需…

Vue项目创建与启动(2023超详细的图文教程)

目录 一、下载node.js 二、下载vue-cli与webpack插件 三、项目初始化(项目配置详细信息) 四、项目启动 五、Vue项目工程结构&#xff08;扩展知识&#xff09; 一、下载node.js 1.检测是否已经安装过node.js 打开控制台,输入 npm -v如果有会显示对应版本 如果没有会显示…

RocketMQ消费者和队列对应关系

参考 RocketMQ 5.0 POP 消费模式探秘 https://www.cnblogs.com/alisystemsoftware/p/15535925.html 旧版本MQ结论 消费者应用和topic队列一对多的关系。 &#xff08;一个消费组consumer group里&#xff0c;一个消费者应用可以消费多个队列的消息。一个队列的消息只能被一个…

矩阵分块例子

有如下矩阵A和B 对A列分块, B行分块后结果如下 对A行分块, B列分块后结果如下

企业网络带宽使用情况检查技巧

想要提高网络性能的企业通常会考虑限制对占用带宽的应用程序&#xff08;如社交媒体和视频流应用程序&#xff09;的访问&#xff0c;但对于那些真正需要获得高效网络的人来说&#xff0c;这还不够&#xff0c;您需要定期跟踪带宽使用情况。 虽然有许多工具可以帮助您检查网络…

Webpack的代码分割(code splitting)

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

干洗店服务预约小程序有什么作用

要说干洗店&#xff0c;近些年的需求度非常高&#xff0c;一方面是人们生活品质提升&#xff0c;另一方面则是各种服饰对洗涤要求提升等&#xff0c;很多人的衣服很多也会通过干洗店进行清洁。 而对从业商家来说&#xff0c;市场庞大一方面需要不断进行市场教育、品牌提升&…

Python自动化测试实战篇:unittest框架详解

为什么要学习unittest 按照测试阶段来划分&#xff0c;可以将测试分为单元测试、集成测试、系统测试和验收测试。单元测试是指对软件中的最小可测试单元在与程序其他部分相隔离的情况下进行检查和验证的工作&#xff0c;通常指函数或者类&#xff0c;一般是开发完成的。 单元…

CMake:构建时为特定目标运行自定义命令

CMake&#xff1a;构建时为特定目标运行自定义命令 导言项目结构相关源码结果 导言 add_custom_command 是 CMake 中用于添加自定义构建规则的命令&#xff0c;通常用于在编译项目时执行一些自定义操作&#xff0c;例如生成文件、运行脚本等。 项目结构 . ├── CMakeLists…

《web前端开发技术》初识Vue + 第一个 Vue程序:hello world

目录 2.1 Vue 简述 2.1.1 什么是 Vue 2.1.2 为什么选择 Vue 2.2 Vue 的三种安装方式 2.1 Vue 简述 Vue 在 JavaScript 前端开发库领域属于后来者&#xff0c;其他前端开发库有 jQuery、ExtJS、 Anguals、React 等。 2.1.1 什么是 Vue &#x1f636;‍&#x1f32b;️Vue (…

树结构及其算法-二叉树遍历

目录 树结构及其算法-二叉树遍历 一、中序遍历 二、后序遍历 三、前序遍历 C代码 树结构及其算法-二叉树遍历 我们知道线性数组或链表都只能单向从头至尾遍历或反向遍历。所谓二叉树的遍历&#xff08;Binary Tree Traversal&#xff09;&#xff0c;简单的说法就是访问树…

Aqua Data Studio 2023.1

为什么选择 Aqua Data Studio&#xff1f; 随着数据在业务中的作用不断发展&#xff0c;组织需要一种有效的方法来简化复杂的技术任务并缩小 IT 和业务团队之间的差距。 使用多个数据库平台不再复杂。使用 Aqua Data Studio 简化您的所有数据管理流程和任务&#xff1a;这是一…