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

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[][],将最上和最左初始化为1for (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.hqwc.cn/news/154708.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redux中的异步操作如何处理

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

通用管理后台项目笔记 - Vue3+Vite安装环境、封装路由

概述 从0打造通用的后台管理系统&#xff0c;系列的第一篇博客&#xff0c;开发环境NodeYarnVite的开发环境&#xff0c;这是记录的学习笔记。 Node环境 本地使用的是Node v18.18.2&#xff0c;npm v9.8.1,安装脚手架工具&#xff0c;npm、cnpm、yarn3种方式&#xff0c;如果…

吉他、班卓琴和贝斯吉他降分器:Arobas Music Guitar 8.1.1

Arobas Music Guitar 是一款专业的吉他、班卓琴和贝斯吉他降分器。在熟练的手中&#xff0c;它不仅可以让您创作&#xff0c;还可以编辑、聆听和录制&#xff0c;以及导入和导出乐谱。如果有人感兴趣的话&#xff0c;录音是在八个轨道上进行的&#xff0c;你可以为每个轨道单独…

SurfaceFliger与Vsync信号如何建立链接?

Vsync信号上报流程 Vsync的注册函数&#xff0c;来临时会回调HWComposer的hook_VSYNC方法&#xff0c;接着调用到vsync方法中 大致流程梳理&#xff1a; 该方法会通知给SurfaceFliger的onVsyncReceived方法&#xff0c;接着调用DispSync的addResyncSample方法。 DispSyncThr…

钢带EPC纠偏比例伺服液压站比例伺服阀控制器

钢带EPC纠偏比例伺服液压站是一种专门用于卷取机的自动化设备&#xff0c;主要目标是确保钢带的一侧边缘保持平直&#xff0c;从而避免在后续运输和处理过程中损坏带钢的边缘。这种系统主要应用于钢铁、橡胶、造纸等行业的非透明带材连续生产线上。 该系统主要由液压源、电液伺…

RK3568-clock

pll锁相环 总线 gating rk3568.dtsi pmucru: clock-controller@fdd00000 {compatible = "rockchip,rk3568-pmucru";reg = <0x0 0xfdd00000 0x0 0x1000>;rockchip,grf = <&grf>;rockchip,pmugrf = <&pmugrf>;#clock-cells = <1>;#re…

【k8s】5、资源管理命令-声明式

目录 一、 yaml和json介绍 1、yuml语言介绍 2、k8s支持的文件格式 3、yaml和json的主要区别 二、声明式对象管理 1、命令式对象配置 2、声明式对象配置 3、声明式对象管理命令介绍 三、编写资源配置清单 1、 编写yaml文件 2、 启动并查看资源 3、创建service服务对外…

第18章_MySQL8其它新特性

第18章_MySQL8其它新特性 讲师&#xff1a;尚硅谷-宋红康&#xff08;江湖人称&#xff1a;康师傅&#xff09; 官网&#xff1a;http://www.atguigu.com 1. MySQL8新特性概述 MySQL从5.7版本直接跳跃发布了8.0版本&#xff0c;可见这是一个令人兴奋的里程碑版本。MySQL 8版…

1300*C. Social Distance(贪心构造)

Problem - 1367C - Codeforces 解析&#xff1a; 统计出所有连续0序列&#xff0c;并且记录其左右两侧有没有1&#xff0c;然后对于四种情况分别判断即可。 #include<bits/stdc.h> using namespace std; int t,n,k; signed main(){scanf("%d",&t);while(…

酷开科技,让家庭更有温度!

生活中总有一些瞬间&#xff0c;会让我们感到无比温暖和幸福。一个拥抱、一句问候、一杯热茶&#xff0c;都能让我们感受到家庭的温馨和关爱。酷开科技也用自己的方式为我们带来了独属于科技的温暖&#xff0c;通过全新的体验将消费者带进一个充满惊喜的世界&#xff0c;让消费…

HCIP——MGRE实验

一、实验要求 1.R5为ISP&#xff0c;只能进行IP地址配置&#xff1b;其所有地址均为公有IP地址 2.R1和R5间使用PPP的PAP认证&#xff0c;R5为主认证方&#xff1b; R2与R5之间使用PPP的chap认证&#xff0c;R5为主认证方&#xff1b; R3与R5之间使用HDLC封装。 3.R1/R2/R3…

linux查看系统版本、内核信息、操作系统类型版本

1. 使用 uname 命令&#xff1a;这将显示完整的内核版本信息&#xff0c;包括内核版本号、主机名、操作系统类型等。 uname -a2. 使用 lsb_release 命令&#xff08;仅适用于支持 LSB&#xff08;Linux Standard Base&#xff09;的发行版&#xff09;&#xff1a;这将显示包含…