【LeetCode】--- 动态规划 集训(二)

目录

  • 一、63. 不同路径 II
    • 1.1 题目解析
    • 1.2 状态转移方程
    • 1.3 解题代码
  • 二、931. 下降路径最小和
    • 2.1 题目解析
    • 2.2 状态转移方程
    • 2.3 解题代码
    • 三、174. 地下城游戏
    • 3.1 题目解析
    • 3.2 状态转移方程
    • 3.3 解题代码

一、63. 不同路径 II

题目地址: 不同路径 II


一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。

输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
在这里插入图片描述
从左上角到右下角一共有 2 条不同的路径:

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

1.1 题目解析

状态表示:

对于这种「路径类」的问题,我们的状态表示⼀般有两种形式

  1. [i, j]位置出发,到达⽬标位置有多少种方式;
  2. 从起始位置出发,到达 [i, j]位置,,⼀共有多少种方式。

这⾥选择第⼆种定义状态表示的方式:dp[i][j]表示:走到 [i, j]位置处,⼀共有多少种方式。

状态转移:

简单分析⼀下。如果 dp[i][j]表示到达 [i, j]位置的方法数,那么到达 [i, j]位置之前的⼀小步,有两种情况:

  1. [i, j]位置的上方( [i - 1, j]的位置)向下走一步,转移到 [i, j]位置;
  2. [i, j]位置的左方( [i, j - 1]的位置)向右⾛⼀步,转移到 [i, j]位置。

但是, [i - 1, j][i, j - 1]位置都是可能有障碍的,此时从上⾯或者左边是不可能到达 [i, j]位置的,也就是说,此时的方法数应该是 0。由此我们可以得出⼀个结论,只要这个位置上「有障碍物」,那么我们就不需要计算这个位置上的值,直接让它等于 0 即可。

初始化:

可以在最前⾯加上⼀个「辅助结点」,帮助我们初始化。使⽤这种技巧要注意两个点:

  1. 辅助结点⾥⾯的值要「保证后续填表是正确的」;
  2. 下标的映射关系」。

在本题中,添加一行,并且添加⼀列后,只需将 dp[1][0]的位置初始化为 1即可。 添加一行和一列是因为[i, j]位置需要[i - 1, j][i, j - 1]两个方位的值,以此确保状态表不越界,将dp[1][0]初始化为1,为了保证在起点时有方法数 1 。

填表顺序:

根据「状态转移」的推导,填表的顺序就是 「从上往下」填每一行,每一行「从左往右」。

返回值:

根据「状态表⽰」,我们要返回的结果是 dp[m][n]

1.2 状态转移方程

从当前位置的上方和左方,到达当前位置的方法数:dp[i][j] = dp[i - 1][j] + dp[i][j - 1];。还需要注意的是若当前位置为障碍物,直接将当前状态置为0(即dp[i][j] = 0;)

1.3 解题代码

class Solution 
{
public:int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {int row = obstacleGrid.size(), col = obstacleGrid[0].size();//1. 创建dp表vector<vector<int>> dp(row + 1, vector<int>(col + 1));//2. 初始化dp[0][1] = 1;//3. 填表//从上到下填表 -> 从左到右填表for (int i = 1; i <= row; ++i)for (int j = 1; j <= col; ++j)if(obstacleGrid[i - 1][j - 1] == 0) dp[i][j] = dp[i][j - 1] + dp[i - 1][j];//4. 返回值return dp[row][col];}
};

二、931. 下降路径最小和

题目地址: 931. 下降路径最小和


给你一个n x n方形 整数数组 matrix ,请你找出并返回通过 matrix 的下降路径最小和
下降路径 可以从第一行中的任何元素开始,并从每一行中选择一个元素。在下一行选择的元素和当前行所选元素最多相隔一列(即位于正下方或者沿对角线向左或者向右的第一个元素)。具体来说,位置(row, col) 的下一个元素应当是(row + 1, col - 1)(row + 1, col)或者 (row + 1, col + 1)

在这里插入图片描述
输入:matrix = [[2,1,3],[6,5,4],[7,8,9]]
输出:13
解释:如图所示,为和最小的两条下降路径

2.1 题目解析

状态表示:

对于这种「路径类」的问题,我们的状态表示一般有两种形式:

  1. 从 [i, j] 位置出发,到达⽬标位置有多少种方式;
  2. 从起始位置出发,到达 [i, j] 位置,⼀共有多少种方式

这里选择第⼆种定义状态表示的方式:
dp[i][j]表示:到达 [i, j]位置时,所有下降路径中的最小和。

状态转移:

对于普遍位置 [i, j],根据题意得,到达 [i, j]位置可能有三种情况

  1. 从正上方 [i - 1, j]位置转移到 [i, j]位置;
  2. 从左上方 [i - 1, j - 1]位置转移到 [i, j]位置;
  3. 从右上方 [i - 1, j + 1]位置转移到 [i, j]位置;

我们要的是三种情况下的「最小值」,然后再加上矩阵在 [i, j]位置的值。于是 dp[i][j] = min(dp[i - 1][j], min(dp[i - 1][j - 1], dp[i - 1][j + 1])) + matrix[i][j]

初始化:

可以在最前⾯加上⼀个「辅助结点」,帮助我们初始化。使用这种技巧要注意两个点:

  1. 辅助结点里面的值要「保证后续填表是正确的」;
  2. 下标的映射关系」。

事实上这题的状态转移方程是不难想到的,而关键问题在于初始化。[i, j]位置的值可以从三个方向来 - 上、左、右,所以为了不越界,在本题中,需要「加上一行」,并且「加上两列」(最左边和最右边)。 所有的位置都初始化为无穷大,然后将第一行初始化为 0 即可。将初始值设为无穷大是因为,这些是虚拟节点,不应该对选数造成影响(即大于其他两路的值)。

填表顺序:

根据「状态表示」,填表的顺序是「从上往下」。

返回值:

注意这里不是返回 dp[m][n]的值!
题⽬要求「只要到达最后一行」就行了,因此这⾥应该返回「 dp 表中最后一行的最小值」。

2.2 状态转移方程

dp[i][j] = min(dp[i - 1][j], min(dp[i - 1][j - 1], dp[i - 1][j + 1])) + matrix[i][j];

2.3 解题代码

class Solution 
{
public:int minFallingPathSum(vector<vector<int>>& matrix) {//1. 创建dp表int row = matrix.size(), col = matrix.size();vector<vector<int>> dp(row + 1, vector<int>(col + 2, INT_MAX));//2. 初始化列表//第一行变为0for(int i = 0; i < col + 2; ++i) dp[0][i] = 0;//3. 填表for(int i = 1; i <= row; ++i)for(int j = 1; j <= col; ++j)dp[i][j] = matrix[i - 1][j - 1] + min(min(dp[i - 1][j - 1], dp[i - 1][j]), dp[i - 1][j + 1]);//4. 返回结果int ans = INT_MAX;for(int i = 1; i <= col; ++i)ans = min(ans, dp[row][i]);return ans;}
};

三、174. 地下城游戏

题目地址: 174. 地下城游戏


恶魔们抓住了公主并将她关在了地下城 dungeon 的 右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。

骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。
有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。
为了尽快解救公主,骑士决定每次只 向右向下 移动一步。
返回确保骑士能够拯救到公主所需的最低初始健康点数。
注意: 任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。

在这里插入图片描述
输入:dungeon = [[-2,-3,3],[-5,-10,1],[10,30,-5]]
输出:7
解释:如果骑士遵循最佳路径:右 -> 右 -> 下 -> 下 ,则骑士的初始健康点数至少为 7 。

3.1 题目解析

状态表示:

这道题如果我们定义成:从起点开始,到达 [i, j]位置的时候,所需的最低初始健康点数。那么我们分析状态转移的时候会有⼀个问题:那就是我们当前的健康点数还会受到后面的路径的影响。也就是从上往下的状态转移不能很好地解决问题。

这个时候我们要换⼀种状态表示:[i, j]位置出发,到达终点时所需要的最低初始健康点数。 这样我们在分析状态转移的时候,后续的最佳状态就已经知晓。

综上所述,定义状态表示为:
dp[i][j]表示:从 [i, j]位置出发,到达终点时所需的最低初始健康点数。

状态转移方程:

对于 dp[i][j],从 [i, j]位置出发,下⼀步会有两种选择(为了方便理解,设 dp[i][j]的最终答案是 x ):

  1. 走到右边,然后走向终点:
    那么我们在 [i, j]位置的最低健康点数加上这⼀个位置的消耗,应该要⼤于等于右边位置的最低健康点数,也就是: x + dungeon[i][j] >= dp[i][j + 1]。通过移项可得: x >= dp[i][j + 1] - dungeon[i][j]。因为我们要的是最小值,因此这种情况下的 x = dp[i][j + 1] - dungeon[i][j]
  2. 走到下边,然后走向终点:
    那么我们在 [i, j]位置的最低健康点数加上这⼀个位置的消耗,应该要大于等于下边位置的最低健康点数,也就是: x + dungeon[i][j] >= dp[i + 1][j]。通过移项可得: x >= dp[i + 1][j] - dungeon[i][j]。因为我们要的是最小值,因此这种情况下的 x = dp[i + 1][j] - dungeon[i][j]

综上所述,我们需要的是两种情况下的最小值,因此可得状态转移方程为:dp[i][j] = min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j]

但是,如果当前位置的 dungeon[i][j]是⼀个比较大的正数的话, dp[i][j]的值可能变成 0 或者负数。也就是最低点数会小于 1 ,那么骑士就会死亡。因此我们求出来的 dp[i][j]如果小于等于 0 的话,说明此时的最低初始值应该为 1 。处理这种情况仅需让 dp[i][j]与 1 取⼀个最大值即可:dp[i][j] = max(1, dp[i][j])

初始化:

可以在最前⾯加上⼀个「辅助结点」,帮助我们初始化。使用这种技巧要注意两个点:

  1. 辅助结点⾥⾯的值要「保证后续填表是正确的」;
  2. 「下标的映射关系」。

在本题中,在 dp 表最后⾯添加一行,并且添加⼀列后,所有的值都先初始化为⽆穷大,然后让dp[m][n - 1] = dp[m - 1][n] = 1即可。

填表顺序:

根据「状态转移方程」,我们需要「从下往上填每一行」,「每一行从右往左」

返回值:

根据「状态表示」,我们需要返回 dp[0][0]的值。

3.2 状态转移方程

骑士到达当前位置最低健康点数:dp[i][j] = min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j]
确保健康值不低于1:dp[i][j] = max(1, dp[i][j]);

3.3 解题代码

class Solution
{public:int calculateMinimumHP(vector<vector<int>>& dungeon) {int m = dungeon.size(), n = dungeon[0].size();// 建表 + 初始化vector<vector<int>> dp(m + 1, vector<int>(n + 1, INT_MAX));dp[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] = min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j];dp[i][j] = max(1, dp[i][j]);}}// 返回结果return dp[0][0];}
};

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

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

相关文章

传输层 --- TCP (下篇)

目录 1. 超时重传 1.1. 数据段丢包 1.2. 接收方发送的ACK丢包 1.3. 超时重传的超时时间如何设置 2. 流量控制 3. 滑动窗口 3.1. 初步理解滑动窗口 3.2. 滑动窗口的完善理解 3.3. 关于快重传的补充 3.4. 快重传和超时重传的区别 4. 拥塞控制 4.1. 拥塞控制的宏观认识…

「 典型安全漏洞系列 」11.身份验证漏洞详解

身份验证是验证用户或客户端身份的过程。网站可能会暴露给任何连接到互联网的人。这使得健壮的身份验证机制成为有效的网络安全不可或缺的一部分。 1. 什么是身份验证 身份验证即认证&#xff0c;是验证给定用户或客户端身份的过程。身份验证漏洞使攻击者能够访问敏感数据和功…

Linux网络基础 (三) —— Socket

文章目录 Socket 编程基本概念Socket背景Socket 为了解决什么问题 socketsockaddr结构sockaddrsockaddr_insockaddr 和 sockaddr_in 的关系sockaddr_un 示例代码 &#x1f396; 博主的CSDN主页&#xff1a;Ryan.Alaskan Malamute &#x1f4dc; 博主的代码仓库主页 [ Gitee ]&…

【MySQL】数据操作语句(DML)

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习计网、mysql和算法 ✈️专栏&#xff1a;MySQL学习 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac…

【论文通读】AgentStudio: A Toolkit for Building General Virtual Agents

AgentStudio: A Toolkit for Building General Virtual Agents 前言AbstractMotivationFramework评估GUI GroudingReal-World Cross-Application Benchmark Suite Conclusion 前言 来自昆仑万象的一篇智能体环境数据大一统框架工作&#xff0c;对未来计算机智能体的发展具有指…

FPGA常用IP核之FIFO学习

IP核是FPGA芯片公司提供的逻辑功能块&#xff0c;在FPGA芯片中可以进行优化和预先配置&#xff0c;可以直接在用户设计的程序中使用&#xff0c;应用范围很广。在FPGA设计开发过程中使用IP核&#xff0c;可以大大的缩短开发周期&#xff0c;高度优化的IP核可以使FPG开发工程师专…

前端三剑客 —— CSS (第六节)

目录 内容回顾&#xff1a; 弹性布局属性介绍 案例演示 商品案例 布局分析 登录案例 网格布局 内容回顾&#xff1a; 变量&#xff1a;定义变量使用 --名称&#xff1a;值&#xff1b; 使用变量&#xff1a; 属性名&#xff1a;var&#xff08;--名称&#xff09;&a…

虚拟主机、VPS主机和云服务器的区别

对于每个建站新手来说&#xff0c;首先要解决的就是服务器购买的问题&#xff0c;目前市面有很多类型的服务器&#xff0c;常见的有&#xff1a;阿里云、腾讯云、Vultr云服务器&#xff0c;也有RackNerd、Cloudways等提供的VPS&#xff0c;还有SiteGround、ChemiCloud 、 Hosti…

数据结构之堆底层实现的循序渐进

题外话 把没写的都补回来! 正题 堆 概念 堆是一棵完全二叉树&#xff0c;因此可以层序的规则采用顺序的方式来高效存储&#xff0c; 大根堆:指根结点比左右孩子都大的堆 小根堆:指根结点比左右孩子都小的堆 性质 1.堆中某个节点的值总是不大于或不小于其父节点的值 2…

鸿蒙OS元服务开发:【(Stage模型)设置应用主窗口】

一、设置应用主窗口说明 在Stage模型下&#xff0c;应用主窗口由UIAbility创建并维护生命周期。在UIAbility的onWindowStageCreate回调中&#xff0c;通过WindowStage获取应用主窗口&#xff0c;即可对其进行属性设置等操作。还可以在应用配置文件中设置应用主窗口的属性&…

每日一题(leetcode1026):节点与其祖先的最大差值--dfs

考虑到只能计算祖先之间的节点差而不能计算兄弟之间的节点差&#xff0c;所以思考使用dfs来解决该题。 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), ri…

嵌入式开发学习---(部分)数据结构(无代码)

数据结构 为什么学习数据结构&#xff1f; 1&#xff09;c语言告诉如何写程序&#xff0c;数据结构是如何简洁高效的写程序 2&#xff09;遇到一个实际问题&#xff0c;需要写程序去实现相应功能&#xff0c;需要解决那两个方面的问题&#xff1f; 如何表达数据之间的逻辑规律…

背包问题---

一、背包模型 有一个体积为V的背包,商店有n个物品,每个物品有一个价值v和体积w,每个物品只能被拿一次,问能够装下物品的最大价值。 这里每一种物品只有两种状态即"拿"或"不拿". 设状态dp[i][j]表示到第i个物品为止,拿的物品总体积为j的情况下的最大价…

一、持续集成介绍

持续集成介绍 一、什么是持续集成二、持续集成的流程三、持续集成的组成要素四、持续集成的好处 一、什么是持续集成 持续集成&#xff08;CI&#xff09;指的是&#xff0c;频繁地&#xff08;一天多次&#xff09;将代码集成到主干。持续集成的目的&#xff0c;就是让产品可…

LeetCode:1483. 树节点的第 K 个祖先(倍增 Java)

目录 1483. 树节点的第 K 个祖先 题目描述&#xff1a; 实现代码与解析&#xff1a; 倍增 原理思路&#xff1a; 1483. 树节点的第 K 个祖先 题目描述&#xff1a; 给你一棵树&#xff0c;树上有 n 个节点&#xff0c;按从 0 到 n-1 编号。树以父节点数组的形式给出&#…

软件测试——黑盒测试

黑盒测试也就是针对功能进行测试&#xff0c;白盒测试就是后端根据自己的代码逻辑进行一下自测&#xff0c;灰盒测试就是黑盒和白盒的混合测试。 1.黑盒测试主要发现以下类型的错误 对比需求规格说明书功能遗漏或者不一致。 接口错误 数据库连接访问错误 效率不高&#xff…

【好书推荐-第十四期】《 互联网大厂晋升指南:从P5到P9的升级攻略》

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主、前后端开发、人工智能研究生。公众号&#xff1a;洲与AI。 &#x1f388; 本文专栏&#xff1a;本文收录…

第八讲 Sort Aggregate 算法

我们现在将讨论如何使用迄今为止讨论过的 DBMS 组件来执行查询。 1 查询计划【Query Plan】 我们首先来看当一个查询【Query】被解析【Parsed】后会发生什么&#xff1f; 当 SQL 查询被提供给数据库执行引擎&#xff0c;它将通过语法解析器进行检查&#xff0c;然后它会被转换…

机器人力觉控制(力源)原理及力矩传感器性能分析

机器人力控原理及其性能分析 在机器人的操作任务中&#xff0c;处理机器人和环境之间的物理接触是非常重要的。由于机器人系统的复杂性和不确定性&#xff0c;纯运动控制往往是不够的&#xff0c;因为即使是最精确的模型也无法完全准确地预测所有可能的情况。 当机器人在与环境…

arm开发板移植工具mkfs.ext4

文章目录 一、前言二、手动安装e2fsprogs1、下载源码包2、解压源码3、配置4、编译5、安装 三、移植四、验证五、总结 一、前言 在buildroot菜单中&#xff0c;可以通过勾选e2fsprogs工具来安装mkfs.ext4工具&#xff1a; Target packages -> Filesystem and flash utilit…