动态规划算法的欢乐密码(二):路径问题

专栏:算法的魔法世界

个人主页:手握风云

一、例题讲解

1.1. 不同路径

        题目要求是计算从网格的左上角(起点)到右下角(终点)的所有不同路径的数量。机器人每次只能向下或向右移动一步。如下图所示,在3*2的网格中,机器人可以“向右、向下、向下”,“向下、向下、向右”,“向下、向右、向下”一共三种路径。

        根据题目要求,本题的状态表示为走到[i, j]为终点的位置时,一共有多少种走法。因为机器人只能向下或者向右走,那么我们要想知道[i, j]的路径数量,就需要直到从起点到达[i-1, j]的路径数和[i, j-1]的路径数,然后再向右走一步或者向下走一步就可以了。注意,这是只是走了一步,而不是走了一个路径。所以状态转移方程为dp[i][j] = dp[i-1][j] + dp[i][j-1]。根据状态转移方程,我们需要根据左侧和上侧的网格来计算,如下图所示的几个边界会出现越界的情况。我们仍然可以按照一维数组的方法增加虚拟节点。接下来就是要填写虚拟节点里的值,保证后面填表的结果是正确的。填表顺序从上到下填写每一行,从左到右填写每一列。

        完整代码实现:

class Solution {public int uniquePaths(int m, int n) {// 创建一个二维数组dp,用于存储到达每个位置的路径数int[][] dp = new int[m + 1][n + 1];// 初始化第一列的路径数为1,因为只能从上往下走dp[0][1] = 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][n];}
}

1.2. 不同路径 II

        本题与上一题一样,计算不同的路径数,每次只能向右或向下移动一步,但某些网格是禁止通行的。
        状态表示与上一题一样,同样是走到[i, j]为终点的位置时,一共有多少种走法。这里因为多了障碍物,如果有障碍物的网格,那么可到达的路径数就是0;如果没有障碍物,那么状态转移方程就是dp[i][j] = dp[i-1][j] + dp[i][j-1]。如果终点的左侧或者上侧有障碍物,我们根本过不来,路径数为0,最后的相加的结果也没影响。初始化与上一题的方法一样,我们只需要保证起点的值正确就可以。这里与上一题不同的是,上一题的参数是两个整数,这道题是一个二维数组,需要注意下标的映射,原始数组的下标与填表时的下标是[i-1][j-1]。填表顺序从上到下,每一行从左到右。


        完整代码实现:

class Solution {public int uniquePathsWithObstacles(int[][] obstacleGrid) {int m = obstacleGrid.length;int n = obstacleGrid[0].length;// 创建一个二维数组dp,用于存储到达每个位置的路径数int[][] dp = new int[m + 1][n + 1];// 初始化dp[1][0]为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];}
}

1.3. 珠宝的最高价值

        题目要求在一个二维矩阵中,从左上角开始,每次只能向右或向下移动,直到到达右下角,找出一条路径使得路径上珠宝的总价值最大。
        首先根据题目要求,本题的状态表示为到达[i, j]位置时,此时珠宝的最大价值。状态表示就是通过最近的一步来分析问题,到达[i, j]的前一个位置:[i-1, j]、[i, j-1]的珠宝最大价值再加上当前珠宝的价值,所以状态转移方程dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]) + frame[i - 1][j - 1]。初始化的时候与上面一样,保证不越界,并且保证后面的填表是正确的,因为我们是要左侧和上面之中的最大值,因为珠宝的价值都是大于等于0的,所以所有的都初始化为0。填表顺序从上到下,每一行从左到右。

        完整代码实现:

class Solution {public int jewelleryValue(int[][] frame) {// 获取框架的行数和列数int m = frame.length;int n = frame[0].length;// 创建一个二维数组dp,用于存储每个位置的最大珠宝价值int[][] dp = new int[m + 1][n + 1];// 遍历框架的每个位置for (int i = 1; i <= m; i++) {for (int j = 1; j <= n; j++) {// 当前位置的最大珠宝价值等于当前位置的珠宝价值加上当前位置左边或上边的最大珠宝价值dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]) + frame[i - 1][j - 1];}}// 返回框架右下角的最大珠宝价值return dp[m][n];}
}

1.4. 下降路径最小和

        给定一个包含整数的二维数组 matrix,要求找到从第一行下降到最后一行的最小路径和。每次下降只能从当前元素移动到下一行的左上、正下、右下三个方向之一。具体来说,对于矩阵中的每个元素matrix[i][j],你可以选择移动到matrix[i+1][j-1]、matrix[i+1][j]或matrix[i+1][j+1],并且需要保证这些移动是在矩阵的范围内。目标是找到从第一行任意元素开始,经过若干次下降后到达最后一行的路径中,路径和最小的值。

        根据题目要求,本题的状态表示为dp[i][j],到达[i,j]位置时的最小下降路径。接下来根据最近的一步划分问题,如下图所示,可以从A、B、C到达最后一行,那么此时的最小路径和分别为dp[i-1][j-1](表示为x)、dp[i-1][j](表示为y)、dp[i-1][j+1](表示为z),到达最后一行的状态表示dp[i][j]为前一个位置的最小路径和再加上它本身,所以状态转移方程为dp[i][j]=min(x,y,z)+matrix[i][j]。

        根据状态转移方程,下图中的几个位置会发生越界。我们可以将dp表新增一行二列,根据状态转移方程,新增前第一行dp表的值都为0,新增后第一行dp表的初始值也为0。为了不影响下一行的填表,必须保证最左侧和最右侧的两列不参与运算,我们可以将两列都初始化为无穷大。下标映射的时候,为了对应原始矩阵的下标,我们需要加上一行和两列,我们一开始就把dp表都初始化为无穷大,然后再把第一行改为0。填表的时候,从上往下即可,不用关心每一行的顺序。返回值要注意,我们只需返回最后一行的最小值即可。

        完整代码实现:

class Solution {public int minFallingPathSum(int[][] matrix) {int n = matrix.length;// 创建一个二维数组dp,用于存储到达每个位置的最小路径和int[][] dp = new int[n + 1][n + 2];// 将dp的第一行和最后一行初始化为最大值,表示无法到达这些位置for (int i = 1; i <= n; i++) {dp[i][0] = dp[i][n + 1] = Integer.MAX_VALUE;}for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {// 计算到达当前位置的最小路径和dp[i][j] = Math.min(dp[i - 1][j], Math.min(dp[i - 1][j - 1], dp[i - 1][j + 1])) + matrix[i - 1][j - 1];}}// 初始化最小路径和为最大值int ret = Integer.MAX_VALUE;// 遍历dp的最后一行,找到最小路径和for (int i = 1; i <= n; i++) {ret = Math.min(ret, dp[n][i]);}return ret;}
}

1.5. 最小路径和

        给定一个包含非负整数的 m x n 网格 grid,找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。每次只能向下或者向右移动一步。返回一个整数,表示所有可能路径中的最小和。

        根据题目要求,本题的状态表示为到达[i,j]位置时,最小的路径和dp[i][j]。根据最近的一步,到达这个终点位置的前一步为它的左侧和上方,那么状态转移方程dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]。根据状态转移方程,下图当中的几个位置会发生越界。为了不影响起点位置的值,我们可以将起点位置的左侧和上方都初始化为0,但起点位置的右上方不能初始化为0,因为起点位置的右侧必须要取最小值,所以我们可以将新增的都初始化为无穷大,再将dp[0][1]和dp[1][0]赋值为0。填表顺序为从上到下,每一行从左到右。返回值直接返回右下角的元素。

        完整代码实现:

class Solution {public int minPathSum(int[][] grid) {// 获取网格的行数和列数int m = grid.length;int n = grid[0].length;// 创建一个二维数组dp,用于存储到达每个位置的最小路径和int[][] dp = new int[m + 1][n + 1];for (int i = 0; i <= m; i++) {dp[i][0] = Integer.MAX_VALUE;}for (int i = 0; i <= n; i++) {dp[0][i] = Integer.MAX_VALUE;}// 初始化dp数组的起点位置,表示从起点到起点的最小路径和为0dp[0][1] = dp[1][0] = 0;for (int i = 1; i <= m; i++) {for (int j = 1; j <= n; j++) {// 到达当前位置的最小路径和等于到达上方位置和左方位置的最小路径和加上当前位置的值dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i - 1][j - 1];}}return dp[m][n];}
}

1.6. 地下城游戏

        给定一个二维的地下城,其中每个格子代表勇士的血量增减。勇士从左上角出发,需要到达右下角的公主所在的格子。勇士在任何时候的血量都不能小于1。请你计算出勇士初始需要的最小血量。

        我们根据示例1推导一下初始血量:假设初始血量HP为3,才能走出第一个房间,但走不出第二个房间;假设HP为6时,能够走出前两个房间,接着吃血包恢复到5,但走到最后右下角房间时,血量为0,还是无法救出公主;因此初始血量必须为7。
        根据前面几题的经验和题目要求:1. 以右下角为结尾,从起点出发到达[i, j]位置时的最低HP。但我们这样定义状态表示是不行的,因为根据示例1的分析,状态表示不光受到前面状态的影响,也会受到后面状态的影响,填表的时候也得需要后面路径选择的影响。2. 反过来,以左上角为起点,从[i, j]到达起点,所需最低的HP。

        接下来根据最近的一步划分问题,设初始血量为x,当骑士向下走或者向右走的时候,我们需要保证他走出这个房间之后HP大于等于最低HP要求,也就是状态转移方程dp[i][j] = min(dp[i + 1][j] - dungeon[i][j], dp[i][j + 1] - dungeon[i][j])。但还有一个细节需要注意,如果dungeon[i][j]太大,一减成了负数,骑士提前死亡,无法吃到血包。所以我们还需要对max(dungeon[i][j],1)。

        初始化的时候,根据状态转移方程,下图这些位置会越界,并且我们只需保证填表正确即可,不需要关注下标的映射关系。填表顺序从下往上填每一行,每一行从右往左。返回值dp[0][0]。

        完整代码实现:

class Solution {public int calculateMinimumHP(int[][] dungeon) {// 获取地图的行数和列数int m = dungeon.length, n = dungeon[0].length;// 创建一个二维数组dp,用于存储从起点到每个点的最小生命值int[][] dp = new int[m + 1][n + 1];// 将dp数组的最后一行和最后一列初始化为最大值for (int i = 0; i <= n; i++) {dp[m][i] = Integer.MAX_VALUE;}for (int i = 0; i <= m; i++) {dp[i][n] = Integer.MAX_VALUE;}// 将dp数组的倒数第二行和倒数第二列初始化为1dp[m][n - 1] = dp[m - 1][n] = 1;// 从倒数第二行和倒数第二列开始,逐行逐列计算每个点的最小生命值for (int i = m - 1; i >= 0; i--) {for (int j = n - 1; j >= 0; j--) {// 当前点的最小生命值为从右边和下边的最小生命值减去当前点的值dp[i][j] = Math.min(dp[i][j + 1], dp[i + 1][j]) - dungeon[i][j];// 如果当前点的最小生命值小于1,则将其设置为1dp[i][j] = Math.max(dp[i][j], 1);}}// 返回起点处的最小生命值return dp[0][0];}
}

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

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

相关文章

嵌入式相关开源项目、库、资料------持续更新中

嵌入式相关开源项目、库、资料------持续更新中 学习初期最难找的就是找学习资料了&#xff0c;本贴精心汇总了一些嵌入式相关资源&#xff0c;包括但不限于编程语言、单片机、开源项目、物联网、操作系统、Linux、计算机等资源&#xff0c;并且在不断地更新中&#xff0c;致力…

图像处理与机器学习项目:特征提取、PCA与分类器评估

图像处理与机器学习项目:特征提取、PCA与分类器评估 项目概述 本项目将完成一个完整的图像处理与机器学习流程,包括数据探索、特征提取、主成分分析(PCA)、分类器实现和评估五个关键步骤。我们将使用Python的OpenCV、scikit-learn和scikit-image库来处理图像数据并实现机器…

MATLAB | 如何使用MATLAB获取《Nature》全部绘图 (附23-25年图像)

文末有全部图片资源 我在两年前更过如何用 MATLAB 爬取 《Nature》全部插图&#xff0c;最近又有人问我有没有下载好的24&#xff0c;25年插图的压缩包&#xff0c;于是又去拿代码运行了一下&#xff0c;发现两年前写的代码今天居然还能用&#xff0c;代码如下&#xff1a; f…

中国老年健康调查(CLHLS)数据挖掘教程(1)--CLHLS简介和数据下载

北京大学“中国老年健康影响因素跟踪调查&#xff08;简称‘中国老年健康调查’&#xff1b;英文名称为Chinese Longitudinal Healthy Longevity Survey (CLHLS)&#xff09;”及交叉学科研究由国家自然科学基金委主任基金应急项目、重大项目、重点项目及国际合作项目。1998-20…

基本多线程编译make命令

背景&#xff1a; 在ffmpeg源码编译的时候要等很久&#xff0c;快下班了&#xff0c;等不及。 解决方法&#xff1a; 使用多线程编译。 make -j{n} 如&#xff1a; make -j8详解&#xff1a;&#xff08;没时间看的可以返回了&#xff01;&#xff09; 在编译 FFmpeg 时使用…

MNIST数据集上朴素贝叶斯分类器(MATLAB例)

MNIST数据集上朴素贝叶斯分类器 Naive Bayes Classification fitcnb Train multiclass naive Bayes model Syntax Mdl fitcnb(Tbl,ResponseVarName) Mdl fitcnb(Tbl,formula) Mdl fitcnb(Tbl,Y) Mdl fitcnb(X,Y) Mdl fitcnb(___,Name,Value) [Mdl,AggregateOptimization…

网站设计小技巧:利用交互设计提升用户体验

现在很多企业朋友都会感觉到&#xff0c;做网站设计掌握不好设计网页的魂&#xff0c;换了很多设计方式可能效果都不理想。蒙特网站专注高端网站建设20多年&#xff0c;基于为华为、字节跳动、海康威视等头部企业打造网站的经验&#xff0c;今天将近期用户比较喜欢的网页设计方…

Github指南-Add .gitignore和Choose a license

Add .gitignore&#xff08;添加忽略文件列表&#xff09; &#x1f4cc; 作用&#xff1a; .gitignore 文件用于告诉 Git 哪些文件或文件夹**不要被上传&#xff08;版本控制&#xff09;**&#xff0c;例如&#xff1a; 编译生成的临时文件&#xff08;如 .exe, .o&#x…

如何打造沉浸式文件操作体验

在操作系统长期运行后&#xff0c;本地文件系统往往会面临一个常见却棘手的问题&#xff1a;元数据管理效率下降&#xff0c;导致用户在海量文件中检索目标内容时出现显著的延迟与操作成本。这种现象在未使用标签化或语义化管理系统的情况下尤为明显。 而 Oversis 的出现&…

企业AI深水区突围:从星辰大海到脚下泥泞的进化论

一、业务价值旅程&#xff1a;从降本增效到价值跃迁 1.1 技术落地的"甜蜜陷阱" 企业在AI应用初期往往陷入"高配用不起&#xff0c;低配用不了"的困境。一台8卡A100服务器每月电费超3万元的成本&#xff0c;对制造业利润形成巨大挤压。即便跨过算力门槛&a…

PostgreSQL的扩展moddatetime

PostgreSQL的扩展moddatetime moddatetime 是 PostgreSQL 的一个内置扩展&#xff0c;用于自动维护表的最后修改时间字段。这个扩展可以自动更新指定字段为当前时间戳&#xff0c;非常适合需要跟踪记录最后修改时间的应用场景。 一、moddatetime 基本功能 核心特性 自动更新…

自己的电脑搭建外网访问网站服务器的步骤

文章目录 PC电脑做网站服务器的步骤1.前言2. 网站服务器系统的安装2.1个人电脑安装IIS&#xff08;Windows7系统安装IIS7.0&#xff09;2.1.1&#xff1a;打开控制面板&#xff0c;给Windows安装插件 2.2网站配置&#xff1a;2.2.1打开网站配置项&#xff1a;2.2.2开始配置&…

基于深度学习的智能语音合成系统:技术与实践

前言 随着人工智能技术的飞速发展&#xff0c;智能语音合成&#xff08;Text-to-Speech, TTS&#xff09;技术已经成为人机交互领域的重要组成部分。从智能助手到有声读物&#xff0c;语音合成技术正在改变我们与数字内容的交互方式。近年来&#xff0c;深度学习技术为语音合成…

铸铁平台的制造工艺复杂而精细

铸铁平台的制造工艺确实复杂而精细。首先&#xff0c;需要选择合适的铸铁材料&#xff0c;通常是灰铸铁或球墨铸铁&#xff0c;以满足平台的强度和耐磨性要求。然后&#xff0c;根据设计要求&#xff0c;制作模具&#xff0c;并在高温下将铁液倒入模具中进行铸造。在铸造过程中…

ArcPy 与 ArcGIS .NET SDK 读取 GDB 要素类坐标系失败?GDAL 外挂方案详解

ArcPy 与 ArcGIS .NET SDK 读取 GDB 要素类坐标系失败&#xff1f;GDAL 外挂方案详解 在ArcGIS Pro中正常显示的坐标系&#xff0c;为何通过ArcPy或.NET SDK却无法正确读取&#xff1f;本文将分享我在处理CGCS2000坐标系时的踩坑经历&#xff0c;以及最终通过GDAL外挂方案解决问…

Zabbix 高可用架构部署方案(2最新版)

Zabbix 高可用架构部署方案&#xff08;MySQL 双 VIPHAProxyNginx&#xff09; 前景提要&#xff1a;使用 MySQL 作为数据库&#xff0c;两个虚拟 IP&#xff08;10.0.0.100 和 10.0.0.200&#xff09;&#xff0c;HAProxy 作为数据库负载均衡&#xff0c;Nginx 作为 Web 访问…

深入解析Linux分页机制:从虚拟内存到物理地址的魔法转换

目录 引言&#xff1a;为什么需要分页机制&#xff1f; 一、分页机制基础概念 1.1 虚拟地址与物理地址 1.2 页与页框 1.3 为什么是4KB&#xff1f; 二、多级页表结构 2.1 为什么需要多级页表&#xff1f; 2.2 x86_64的四级页表结构 2.3 页表项详解 三、Linux分页实现机…

使用python进行图像处理—图像变换(6)

图像变换是指改变图像的几何形状或空间位置的操作。常见的几何变换包括平移、旋转、缩放、剪切&#xff08;shear&#xff09;以及更复杂的仿射变换和透视变换。这些变换在图像配准、图像校正、创建特效等场景中非常有用。 6.1仿射变换(Affine Transformation) 仿射变换是一种…

NLP-数据集介绍(并不全,文本类介绍)

目录 第一章 STS&#xff08;语义文本相似度&#xff09; &#xff08;重点&#xff09;一、SemEval STS 年度任务&#xff08;2012-2017&#xff09;1. SemEval-2012 STS2. SemEval-2013 STS3. SemEval-2014 STS4. SemEval-2015 STS5. SemEval-2016 STS6. SemEval-2017 STS 二…

JS进阶 Day01

1.作用域和作用域链 let不可访问 var可访问&#xff0c;因为没有块作用域这一说法 2.JS垃圾回收机制以及算法 下图如上图同理 下图这个三个相互引用的&#xff0c;根部找不到&#xff0c;就进行清除。 3.JS闭包 4.变量和函数提升(了解) 5.函数剩余参数和展开运算符 还有种写法 …