【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.rhkb.cn/news/298985.html

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

相关文章

腾讯云(CVM)托管进行权限维持

前言 刚好看到一个师傅分享了一个阿里云ECS实战攻防&#xff0c;然后想到了同样利用腾讯云CVM的托管亦可实现在实战攻防中的权限维持。 简介 腾讯云自动化助手&#xff08;TencentCloud Automation Tools&#xff0c;TAT&#xff09;是一个原生运维部署工具&#xff0c;它可…

“Linux 三剑客”,通常指的是三个经典的命令行工具:grep、sed 和 awk

1、grep&#xff1a; 简介&#xff1a;grep 是一个强大的文本搜索工具&#xff0c;可以用于在文件中查找匹配特定模式的行。示例&#xff1a; 搜索包含特定关键词的行&#xff1a; grep "keyword" filename 递归搜索目录下所有文件&#xff1a; grep -r define zj…

java面试题(Redis)

事情干的差不多了&#xff0c;开刷面试题和算法&#xff0c;争取在短时间内快速成长&#xff0c;理解java面试的常见题型 一、redis使用场景&#xff1a; 缓存&#xff1a;穿透、击穿、雪崩 双写一致、持久化 数据过期、淘汰策略 分布式锁&#xff1a;setnx、redisson 计数…

Flutter Boost 3

社区的 issue 没有收敛的趋势。 设计过于复杂&#xff0c;概念太多。这让一个新手看 FlutterBoost 的代码很吃力。 这些问题促使我们重新梳理设计&#xff0c;为了彻底解决这些顽固的问题&#xff0c;我们做一次大升级&#xff0c;我们把这次升级命名为 FlutterBoost 3.0&am…

Redis -- 缓存穿透问题解决思路

缓存穿透 &#xff1a;缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在&#xff0c;这样缓存永远不会生效&#xff0c;这些请求都会打到数据库。 常见的解决方案有两种&#xff1a; 缓存空对象 优点&#xff1a;实现简单&#xff0c;维护方便 缺点&#xff1a; 额外…

讲讲你对数据结构-线性表了解多少?

线性表 - 数组和矩阵 当谈到线性表时&#xff0c;数组和矩阵是两种常见的数据结构。 数组&#xff08;Array&#xff09;&#xff1a; 数组是有序的元素集合&#xff0c;可以通过索引来访问和操作其中的元素。它是最简单、最基本的数据结构之一。数组的特点包括&#xff1a; …

paddlepaddle模型转换onnx指导文档

一、检查本机cuda版本 1、右键找到invdia控制面板 2、找到系统信息 3、点开“组件”选项卡&#xff0c; 可以看到cuda版本&#xff0c;我们这里是cuda11.7 cuda驱动版本为516.94 二、安装paddlepaddle环境 1、获取pip安装命令 &#xff0c;我们到paddlepaddle官网&#xff…

2012年认证杯SPSSPRO杯数学建模C题(第二阶段)碎片化趋势下的奥运会商业模式全过程文档及程序

2012年认证杯SPSSPRO杯数学建模 C题 碎片化趋势下的奥运会商业模式 原题再现&#xff1a; 从 1984 年的美国洛杉矶奥运会开始&#xff0c;奥运会就不在成为一个“非卖品”&#xff0c;它在向观众诠释更高更快更强的体育精神的同时&#xff0c;也在攫取着巨大的商业价值&#…

idea2023.2.1 java项目-web项目创建-servlet类得创建

如何创建Java项目 1.1 方式1&#xff1a; 1.2 方式&#xff1a; 1.3 方式 如何创建web项目 方式 ----- 推荐 如何创建servlet类 复制6 中得代码 给servlet 配置一个路径 启动tomcat 成功了

【星海随笔】Ubuntu22.04忘记密码

服务器篇&#xff1a; 有问题可留言。 第一步 远程console界面进入该设备 并重启该设备 如果看到这个界面情况 则点击右上角按钮 【发送 CtrlAltDelete】 调出grub启动菜单 NOTE&#xff1a;启动的后半段去点击这个按钮&#xff0c;前半段一直点会一直重启 如果是直连服务器&a…

Linux-4 gcc和makefile

Linux编译器-gcc/g使用 1.设计样例 c语言&#xff1a;linux中用的stdc99版本--可能会出现其他问题 c&#xff1a;Linux中用的stdc11--使用c11版本 Linux没有文件格式的区分&#xff0c;但是编译器区分 gcc编译器的文件格式是filename.c g编译器的文件格式是filename.cc或者fil…

docker的安装及入门指令

目录 一、将docker安装到云服务器步骤 1.更新系统yum版本 2.安装所需依赖 3.添加docker仓库设置(使用的是阿里云) 4.安装docker引擎 5.启动docker并开启自动启动 6. 检查是否安装成功&#xff0c;成功会显示相应版本&#xff0c;否则安装失败 二、docker常用命令 1.从…

Javascript/Node.JS中如何用多种方式避免属性为空(cannot read property of undefined ERROR)

>>>>>>问题 "cannot read property of undefined" 是一个常见的 JavaScript 错误&#xff0c;包含我在内很多人都会遇到&#xff0c;表示你试图访问一个未定义&#xff08;undefined&#xff09;对象的属性。这通常是因为你在访问一个不存在的对象…

【第十六篇】使用BurpSuite实现匹配替换(实战案例)

在Burp中可配置匹配和替换规则,当我们使用浏览器请求程序时,这些规则会自动修改我们的请求和响应。 在某些环境下,我们可以修改 IP 地址,让服务器相信我们属于其本地网络,从而实现与原本无法访问的内部基础设施进行通信。下面将以IP欺骗为例进行操作讲解。 如图,admin目…

2024.4.2-[作业记录]-day07-CSS 盒子模型(显示模式、盒子模型)

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 作业 2024.4.2 学习笔记CSS标签元素显示模式1 块元素2 行内元素3 行内块元素4…

蓝桥杯每日一题:公约数(gcd)

题目描述&#xff1a; 给定两个正整数 a 和 b。 你需要回答 q 个询问。 每个询问给定两个整数 l,r&#xff0c;你需要找到最大的整数 x&#xff0c;满足&#xff1a; x 是 a和 b 的公约数。l≤x≤r。 输入格式 第一行包含两个整数 a,b。 第二行包含一个整数 q。 接下来…

Java 设计模式系列:备忘录模式

简介 备忘录模式是一种软件设计模式&#xff0c;用于在不破坏封闭的前提下捕获一个对象的内部状态&#xff0c;并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。 备忘录模式提供了一种状态恢复的实现机制&#xff0c;使得用户可以方便地回到一个特定…

Mahalanobis距离(马氏距离)的本质

马氏距离是加权 ℓ 2 \ell_2 ℓ2​范数的特例。 马氏距离是一种基于样本分布的距离&#xff0c;加权矩阵是样本或总体协方差矩阵的逆&#xff0c;其本质为去相关数据标准化&#xff0c;通过数据变换&#xff0c;消除样本中不同特征维度间的相关性和量纲差异。

具有温度系数(Temperature)的Softmax函数

Softmax 函数 softmax 函数是一种激活函数&#xff0c;通常用作神经网络最后一层的输出函数。该函数是两个以上变量的逻辑函数的推广。 Softmax 将实数向量作为输入&#xff0c;并将其归一化为概率分布。 softmax函数的输出是与输入具有相同维度的向量&#xff0c;每个元素的…

hbuilderX创建的uniapp项目转移到vscode

场景&#xff1a;一直使用hbuilderX开发的朋友想转移到vscode获取更好的TypeScript支持&#xff0c;所以想把整个项目目录拖到vscode进行开发&#xff0c;但发现运行不了&#xff0c;提示没有package.json等&#xff0c;并且不能执行pnpm命令 首先&#xff0c;我们先来看一下h…