【LeetCode】 动态规划 刷题训练(三)

news2024/11/19 5:37:03

文章目录

  • 931. 下降路径最小和
    • 题目解析
    • 状态转移方程
    • 完整代码
  • 64. 最小路径和
    • 题目解析
    • 状态转移方程
    • 完整代码
  • 174. 地下城游戏
    • 题目解析
    • 状态转移方程
    • 完整代码

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
解释:如图所示,为和最小的两条下降路径

题目解析

当处于 (row,col)位置处时,下一行 可以选择 (row+1,col)位置 / (row+1,col-1)位置 /(row+1,col+1)位置处的元素

状态转移方程

dp[i][j]:表示从第一行位置开始到 [i,j]位置处的时候,最小的下降路径

根据最近的一步,划分问题


[i,j]位置可以由 [i-1,j-1]位置 / [i-1,j]位置 / [ i-1,j+1]位置 向下移动得到

所以可以分为三种情况:


第一种情况:
从 [i-1,j-1]位置 向下移动到到 [i,j] 位置
想要得到[i,j]位置的最小下降路径,就应该先得到[i-1,j-1]位置的最小下降路径 即 dp[i-1,j-1]
再加上[i,j]位置的路径 即 ob[i,j]
第一种情况下 [i,j]位置的最小下降路径为 : dp[i-1,j-1]+ob[i,j]


第二种情况:
从 [i-1,j]位置 向下移动到到 [i,j] 位置
想要得到[i,j]位置的最小下降路径,就应该先得到[i-1,j]位置的最小下降路径 即 dp[i-1,j]
再加上[i,j]位置的路径 即 ob[i,j]
第二种情况下 [i,j]位置的最小下降路径为 : dp[i-1,j]+ob[i,j]


第三种情况:
从 [i-1,j+1]位置 向下移动到到 [i,j] 位置
想要得到[i,j]位置的最小下降路径,就应该先得到[i-1,j+1]位置的最小下降路径 即 dp[i-1,j+1]
再加上[i,j]位置的路径 即 ob[i,j]
第三种情况下 [i,j]位置的最小下降路径为 : dp[i-1,j+1]+ob[i,j]


状态转移方程:
dp[i][j]= min( dp[i-1][j-1],dp[i-1][j],dp[i-1][j+1] )+ob(i,j);

完整代码

class Solution {
public:
    int minFallingPathSum(vector<vector<int>>& ob) {
       
       int m=ob.size();
       int n=ob[0].size();
       //dp数组 扩列一行 两列
       //并将 m+1 个 vetcor 数组 的n+2个值 都初始化为正无穷大
       vector<vector<int>>dp(m+1,vector<int>(n+2,INT_MAX));
       //将dp 扩列的第一行初始化为0
      // dp[0].resize(n+2,0);
      int i=0;
       int j=0;
      for(j=0;j<n+2;j++)
      {
          dp[0][j]=0;
      }
       
       for(i=1;i<=m;i++)
       {
           //从[1,1]位置开始到[i,n]位置结束
           for(j=1;j<=n;j++)
           {
               //ob作为原数组,dp作为扩列数组
               //使用dp扩列的下标 寻找ob对应的原数组下标 行需减1 列减1
               dp[i][j]= min(min(dp[i-1][j-1],dp[i-1][j]),dp[i-1][j+1])+ob[i-1][j-1];
           }
       }

     //寻找dp数组的最后一行的最小值
      int minsize=INT_MAX;
      for(j=1;j<=n;j++)
      {
         if(minsize>dp[m][j])
         {
             minsize=dp[m][j];
         }
      } 
     // 返回dp数组的最后一行的最小值
      return minsize;
    }
};

对于原数组来说,在蓝色区域处使用状态转移方程会发生越界问题,所以通过扩列的方法来解决这个问题


原数组的第一行,只能从当前位置到当前位置,所以储存原始数组元素的值
为了保护影响原数组的第一行的值,所以扩列后的数组第一行 都为0

剩余扩列的位置,若初始化为0,则干扰比较结果,所以为了不影响选取,将其 值 设置为正无穷大


如:6作为[i,j]位置 ,想要取得最小路径,则向下寻找,理应取到2位置处,
但是由于扩列后出现的0,就会选取0,从而导致结果错误

64. 最小路径和

点击查看:最小路径和


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

输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。

题目解析

每次只能向下或者 向右走
从左上角 到右下角 的路径 中 寻找 最小路径和
图中最小路径为: 1+3+1+1+1=7


状态转移方程

dp[i][j] :表示 从起点位置(左上角)到 [i,j]位置 的时候, 此时的最小路径和

根据最近的一步,划分问题


想要到达[i,j]位置,只能从[i-1,j]位置向下走一步得到
或者 从[i,j-1]位置 向右走一步 得到

所以dp[i][j]划分为两种情况:


第一种从[i-1,j]位置向下得到[i,j]位置

想要得到[i,j]位置的最小路径 就先需要得到 [i-1,j]位置的最小路径 即dp[i-1,j]
再加上原数组ob 对应[i,j]位置的值 即ob[i,j]
第一种情况 下[i,j]位置的最小路径和为: dp[i-1,j]+ob[i,j]


第二种 从[i,j-1]位置 向右走一步 得到[i,j]位置

想要得到[i,j]位置的最小路径 就先需要得到 [i,j-1]位置的最小路径 即dp[i,j-1]
再加上原数组ob 对应[i,j]位置的值 即ob[i,j]
第二种情况 下[i,j]位置的最小路径和为: dp[i,j-1]+ob[i,j]


状态转移方程为:
dp[i][j] = min( dp[i-1][j],dp[i][j-1] )+ob[i,j];

完整代码

class Solution {
public:
    int minPathSum(vector<vector<int>>& ob) {
          int m=ob.size();//行
          int n=ob[0].size();//列
          //将m+1个 vector数组 的n+1个值 设置为正无穷大
          //dp数组 将ob原数组 扩一行 和一列
          vector<vector<int>>dp(m+1,vector<int>(n+1,INT_MAX));
          int i=0;
          int j=0;
          //起点位置对应的上一个位置和左一个位置设置为0
          dp[0][1]=0;
          dp[1][0]=0;

          for(i=1;i<=m;i++)
          {
              for(j=1;j<=n;j++)
              {
                  //ob作为原数组 dp作为扩列数组
                  //通过扩列数组的下标 寻找原数组对应的下标 需行减1 列减1
                  dp[i][j]=min(dp[i-1][j],dp[i][j-1])+ob[i-1][j-1];
              }
          }
         // 由于dp是扩列数组 返回右下角 
          return dp[m][n];
          
    }
};

初始化
若使用状态转移方程,则原数组的第一行和第一列都有可能出现越界问题,所以为了避免这个问题,将原数组扩一行和一列

在这里插入图片描述

因为此时并没有上一个位置或者左一个位置,所以dp 数组起点位置(start)的值应为 原数组内对应起点位置的值
为了不影响结果,将start对应的上一个位置和左一个位置都设置为0


红色区域 的上一个值若设置为0,则会进行状态转移方程时,会取到这个0,干扰结果,
所以为了不影响结果,将其设置为正无穷大


剩余的位置也同上述一个原因,会干扰结果,所以为了避免影响结果,都设置为正无穷大

174. 地下城游戏

点击查看:地下城游戏


恶魔们抓住了公主并将她关在了地下城 dungeon 的 右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。
骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。
有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。
为了尽快解救公主,骑士决定每次只 向右 或 向下 移动一步。
返回确保骑士能够拯救到公主所需的最低初始健康点数。

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

题目解析

从左上角 开始 到 右下角 结束
每次只能 向下或者 向右走

-2 -> -3 -> 3 -> 1 -> -5


第一房间时,就会损失2点健康点数,所以骑士想从第一个房间走出来 就需要3点健康点数,但是此时进入第二个房间,还要损失3点健康点数,骑士直接挂掉了 ,所以 初始3点健康点数不可以

将前两个房间走出来,就需要 骑士 起始健康点数为6点(健康点数为0就挂掉了),此时骑士健康点数为1点,
当走完第三个房间时 ,骑士加了3点健康点数,变为4点
当走完第四个房间时 ,骑士加了1点健康点数,变为5点
当走完走到最后一个房间时, 损失5点健康点数 ,骑士健康点数为0,直接挂掉了

所以骑士初始血量应为7点

状态转移方程

因为是通过初始血量判断的,而且不仅受到上面 还有后面的影响
所以要 以某一个位置 为起点 ,来解决问题

dp[i][j] 表示:以[i,j]位置出发,达到终点,存储的值为所需最低初始健康点数

根据最近的一步,划分问题


[i,j]位置,可以向下走一步达到[i+1,j]位置 或者 向右 走一步 达到 [i,j+1]位置

dp[i][j]分为两种情况:


第一种情况为 从[i,j]位置 向右移动到[i,j+1]位置

从[i,j]位置走出来 的健康点数 可以保证[i,j+1] 位置 走到终点
即 dp[i][j] +ob[i][j] >= dp[i][j+1]
dp[i][j]>= dp[i][j+1]-ob[i][j]
而dp[i][j]作为最低健康点数,所以 dp[i][j]=dp[i][j+1]-ob[i][j]


第 二 种情况为 从[i,j]位置 向下移动到[i+1,j]位置

从[i,j]位置走出来 的健康点数 可以保证[i+1,j] 位置 走到终点
dp[i][j] +ob[i][j] >= dp[i+1][j]
dp[i][j]>= dp[i+1][j]-ob[i][j]
而dp[i][j]作为最低健康点数,所以 dp[i][j]=dp[i+1][j]-ob[i][j]


状态转移方程为:
dp[i][j] = min(dp[i][j+1],dp[i+1][j])-ob[i][j];


若ob[i][j]过大,导致dp[i][j]的值为负数,就不符合要求,因为最低健康点数 为1

dp[i][j]=max(1,dp[i][j]);
若dp[i][j]为负数,就更换为1

完整代码

class Solution {
public:
    int calculateMinimumHP(vector<vector<int>>& ob) {
      int m=ob.size();
      int n=ob[0].size();
      // 将 m+1个 vector 数组 的 n+1个值 都置为 正无穷大 
      vector<vector<int>>dp(m+1,vector<int>(n+1,INT_MAX));
     
    //从end位置走出来至少剩下1个健康点数
      dp[m-1][n]=1;
      dp[m][n-1]=1;
      int i=0;
      int j=0;
      for(i=m-1;i>=0;i--)
      {
          for(j=n-1;j>=0;j--)
          {
                dp[i][j]=min(dp[i][j+1],dp[i+1][j])-ob[i][j];
                //若dp[i][j]为负,将其置为1
                dp[i][j]=max(1,dp[i][j]);
          }
      }
      //dp[0][0]表示从起点位置开始,到终点至少需要多少初始健康点数
      return dp[0][0];
    }
};

初始化
根据状态转移方程,最后一行和最右行 都会触发越界问题,所以将原数组进行 扩一行 和扩一列

从end位置才走出来后,一定至少剩下1点健康点数
可能向下走一步,或者向右走一步
从[i,j]位置走出来 的健康点数 可以保证[i,j+1] 位置 走到终点
所以两个位置 都设置为 1


当前红色区域位置,是需要比较它位置的下一个位置和右一个位置 取其中的小的那个位置,
但是下面的位置是虚拟的,所以不能算上,否则会干扰结果
所以其位置设置为 正无穷大


剩余的位置也同上述一个原因,会干扰结果,所以为了避免影响结果,都设置为正无穷大

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

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

相关文章

chatgpt赋能python:Python搜题软件:为你量身定制的智能搜索助手

Python搜题软件&#xff1a;为你量身定制的智能搜索助手 随着互联网的普及&#xff0c;我们每天需要面对海量的信息&#xff0c;其中包含了我们所需要的一切答案。但是&#xff0c;如何快速而准确地找到这些答案呢&#xff1f;这时候&#xff0c;一款智能化的搜题软件就非常有…

【不推荐】win 安装 rust 1.70 (MSVC)

目录 一、下载二、安装三、配置环境变量四、检查是否安装成功五、参考文章 一、下载 官网地址&#xff1a;https://www.rust-lang.org/zh-CN/ https://forge.rust-lang.org/infra/other-installation-methods.html 历史版本下载地址&#xff1a; 二、安装 注意&#xff1a;安…

Redis:redis基于各大实战场景下的基本使用

文章目录 前言String 命令实战1.业务缓存对应redis中的指令伪代码 2.分布式锁对应redis中的指令伪代码 3.限流对应redis中的指令伪代码 List 命令实战1.提醒功能对应Redis中的指令伪代码 2.热点列表对应Redis中的指令伪代码 Hash 命令实战1.用户资料缓存对应redis中的指令伪代码…

RabbitMQ学习笔记(尚硅谷)

文章目录 一. 消息队列1. 定义2. 作用2.1 流量消峰2.2 应用解耦2.3 异步处理 3. 分类4. MQ的选择5. RabbitMQ5.1 概念5.2 四大概念5.3 六大模式5.4 RabbitMQ 工作原理5.5 安装 6. 代码实现 二. Hello World (简单模式)1. 生产者代码2. 消费者代码 三. Work Queues (工作队列模式…

OUC编译原理实验报告 实验5:用Yacc设计语法分析器1 实验6:用Yacc设计语法分析器2

编译原理实验报告 实验5&#xff1a;用Yacc设计语法分析器1 实验6&#xff1a;用Yacc设计语法分析器2 中国海洋大学编译原理实验2023春 仅供同学参考思路 请勿直接抄袭 否则可能喜提0分 目录 文章目录 编译原理实验报告目录一.实验目的二.实验内容实验5实验6 三.实验要求实…

RISCV Reader笔记_2 RV32I

RV32I 完整的RV32I指令集可以用下面的式子中出现单词的首字母表示&#xff1a; 比如这一条&#xff1a; set less than {immediate} {unsigned} 也就是slt slti sltu sltiu这4个指令。 RISCV指令格式如下。R 寄存器操作&#xff0c;I 立即数或load访存&#xff0c;S store访…

C51单片机期末复习第八章单片机接口技术

一 总线&#xff1a; 传送同类信息的连线 三总线&#xff1a; 地址总线AB&#xff0c;数据总线DB,控制总线CB 目录(ppt给的没啥用&#xff0c;乱还不全)&#xff1a; 8.1 单片机的系统总线 8.2 简单并行I/O口扩展 8.3 可编程并行I/O口扩展 8.4 D/A转换与DAC0832应用 8…

稀疏表:最大公约数

问题描述 给定一个数组, 每次操作可以选择数组中任意两个相邻的元素 x , y x, y x,y 并将其 中的一个元素替换为 gcd ⁡ ( x , y ) \operatorname{gcd}(x, y) gcd(x,y), 其中 gcd ⁡ ( x , y ) \operatorname{gcd}(x, y) gcd(x,y) 表示 x x x 和 y y y 的最大公约数。 请…

MIT 6.S081 教材第五章内容 -- 中断与设备驱动--下

MIT 6.S081 教材第五章内容 -- 中断与设备驱动--下 引言关于RISC-V特权级架构说明RISC-V特权模式OpenSBI介绍RISC-V启动过程RISC-V中的异常M模式下的异常1. 硬件中断的处理&#xff08;以时钟中断为例&#xff09;2. M模式下的异常相关寄存器3. 同步异常的处理 S模式下的异常1.…

chatgpt赋能python:Python散点图介绍:如何用Python绘制散点图?

Python散点图介绍&#xff1a;如何用Python绘制散点图&#xff1f; Python是一门流行的编程语言&#xff0c;用于解决各种问题和编写各种应用程序。其中&#xff0c;数据可视化是Python应用程序中非常重要的组成部分。散点图是最常用的数据可视化图形之一&#xff0c;它能够清…

拓扑排序:神经网络

题目链接 神经网络 题目大意 在兰兰的模型中&#xff0c;神经网络就是一张有向图&#xff0c;图中的节点称为神经元&#xff0c;而且两个神经 元之间至多有一条边相连&#xff0c;下图是一个神经元的例子&#xff1a; 图中&#xff0c; X 1 — X 3 X_1—X_3 X1​—X3​是信…

从类加载到双亲委派:深入解析类加载机制与 ClassLoader

目录 前言Class 文件介绍如何生成 class 文件观察 Bytecode 方法class 文件到底是什么样的呢&#xff1f; Class 加载、链接、初始化加载、类加载器双亲委派Launcher 核心类ClassLoader 相关源码ClassLoader 相关问题自定义简单 ClassLoader自定义加密 ClassLoader打破双亲委派…

动态ip与静态ip的概念、区别、应用场景

动态ip与静态ip的区别 前言一、介绍IP地址的概念和作用1.1、IP地址的定义1.2、IP地址的作用 二、动态IP和静态IP的区别2.1、动态IP和静态IP的定义2.2、动态IP和静态IP的特点2.3、动态IP和静态IP的优缺点比较 三、动态IP和静态IP的应用场景3.1. 动态IP的应用场景3.2. 静态IP的应…

利用numpy解决解方程组的基本问题

1 问题 进入大学&#xff0c;我们接触了线性代数&#xff0c;利用线性代数解方程组比高中慢慢计算会好了许多&#xff0c;快捷许多&#xff0c;我们作为编程人员&#xff0c;有没有用python解决解方程组的办法呢&#xff1f; 2 方法 我们提出使用python的numpy解方程。 找到用于…

11- C程序的组成结构 (C语言)

一、C程序的基本组成结构 1、源文件: 后缀为.c 的文件2、头文件&#xff1a;后缀为.h的文件 注意&#xff1a; 源文件 功能&#xff1a;实现程序功能头文件 功能&#xff1a;函数的声明、全局变量的声明、宏定义、类型的声明一个由C语言所组成的项目中 只允许有一个main函数 …

离散数学大作业任务书

目 录 实际的练习题目、系统的总功能和各子模块的功能………………………………………………………………………………1 1.1题目及问题描述………………………………………………………………1 1.2功能概述………………………………………………………………………1 1.3技…

02 | 日志系统:一条SQL更新语句是如何执行的?

以下内容出自《MySQL 实战 45 讲》 02 | 日志系统&#xff1a;一条SQL更新语句是如何执行的&#xff1f; 查询语句的那套流程&#xff0c;更新语句也会走一遍。 更新流程中和查询不一样的是&#xff0c;更新流程中涉及了两个重要的日志模块。redo log (重做日志) 和 binglog&a…

如何编写用于Neo-Hookean材料的Abaqus VUMAT Fortran子例程

引言 大家好&#xff0c;我是一个热爱编程、研究有限元分析的普通程序员。我非常感谢你们能够抽出宝贵的时间来阅读我的文章&#xff0c;你们的支持是我前行的动力。今天&#xff0c;我们将讨论一个非常专业的话题&#xff0c;即如何编写用于Neo-Hookean材料的Abaqus VUMAT Fo…

Unreal 5 实现UI制作

这一篇讲解一下unreal engine里面的内置ui插件UMG&#xff0c;虚幻示意图形界面设计器&#xff08;Unreal Motion Graphics UI Designer&#xff09;(UMG) 是虚幻引擎内置的一套ui制作工具&#xff0c;通过它我们能够实现平面ui&#xff0c;场景hud内容 实现背景图片填充整个…

【MySQL数据管理】:插入、修改、删除操作

前言 ✨欢迎来到小K的MySQL专栏&#xff0c;本节将为大家带来MySQL数据插入、修改、删除的讲解✨ 目录 前言一、插入数据二、修改数据三、删除数据四、总结 一、插入数据 使用INSERT INTO语句来向表中插入数据 ✨语法&#xff1a; 给指定字段添加数据 INSERT INTO 表名 (字段…