动态规划课堂3-----简单多状态问题(买卖股票最佳时机)

目录

引入:

例题1:按摩师(打家劫舍I)

例题2:打家劫舍II

例题3:删除并获得点数

例题4:粉刷房子

例题5:买卖股票的最佳时机含冷冻

结语:


引入:

相信看到这里的友友们对动态规划已经有了一定的了解,下面我将介绍动态规划的简单多状态dp问题。所谓多状态就是在一步下有不同的情况要区分(例如买股票,今天可以分为买还是不买的多两种情况)。由于算法只讲知识点是远远不够的故下面我会在例题中穿插知识点帮助理解。动态规划一般的解题步骤还是1. 状态表示,2.状态转移方程,3.初始化,4.填表顺序,5.返回值。在写代码时一定要把这5步考虑清楚再写代码。

例题1:按摩师(打家劫舍I)

链接:按摩师

题目简介:

一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。

解法(动态规划):

1. 状态表示:

对于简单的线性dp ,我们可以用「经验+题⽬要求」来定义状态表示例如:

(1)以某个位置为结尾的最小花费。

(2)以某个位置为起点到终点的最小花费。

故我们这题采用常用的方式:dp[i] 表⽰:选择到i 位置时,此时的最⻓预约时⻓。由于我们这个题在i 位置的时候,会⾯临选择或者不选择两种抉择,所依赖的状态需要细分。

下面之所以用f和g是因为高中我们所学的f(x)和g(x),可以换成其他的(但最好用f和g就当作是不成文的规定,这样代码的可读性会更高)。

(1)f[i] 表示:选择到i 位置时, nums[i] 必选,此时的最⻓预约时⻓。

(2)g[i]表示:选择到i 位置时, nums[i] 不选,此时的最⻓预约时⻓。

2.状态转移方程

因为状态表示定义了两个,因此我们的状态转移⽅程也要分析两个:

对于f[i] :

如果nums[i] 必选,那么我们仅需知道i - 1 位置在不选的情况下的最⻓预约时⻓, 然后加上nums[i] 即可,因此f[i] = g[i - 1] + nums[i] 。

对于g[i] :

如果nums[i] 不选,那么i - 1 位置上选或者不选都可以。因此,我们需要知道i - 1 位置上选或者不选两种情况下的最⻓时⻓,因此g[i] = max(f[i - 1], g[i - 1]) 。

在状态转移方程这里可以画图来帮助我们理解

3.初始化

这道题的初始化⽐较简单,因此⽆需加辅助节点(前两篇文章已解释),仅需初始化f[0] = nums[0], g[0] = 0 即可。

4.填表顺序

根据「状态转移⽅程」得「从左往右,两个表⼀起填」。

5.返回值

根据「状态表示」,应该返回max(f[n - 1], g[n - 1]) 。

代码实现如下:

class Solution {public int massage(int[] nums) {//1.创建dp表//2.初始化//3.填表//4.返回值int n = nums.length;if(n == 0){return 0;}int[] f = new int[n];//选择int[] g = new int[n];//不选择f[0] = nums[0];for(int i = 1;i < n;i++){f[i] = g[i - 1] + nums[i];g[i] = Math.max(g[i - 1],f[i - 1]);}return Math.max(g[n -1],f[n - 1]);}
}

时间复杂度:O(n)

空间复杂度:O(n)

例题2:打家劫舍II

链接:打家劫舍II

题目简介:

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。

给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。

 解法(动态规划):

通过阅读题目友友可以发现这题和例题一是差不多的,唯一不同就是例题二加了一个末尾和开头的限制。上⼀个问题是⼀个「单排」的模式,这⼀个问题是⼀个「环形」的模式,也就是⾸尾是相连的。但是我们可以将「环形」问题转化为「两个单排」问题:

(1)偷第⼀个房屋时的最大⾦额x ,此时不能偷最后⼀个房⼦,因此就是偷[0, n - 2] 区间的房⼦。

(2)不偷第⼀个房屋时的最大⾦额y ,此时可以偷最后⼀个房⼦,因此就是偷[1, n - 1] 区 间的房⼦。

两种情况下的「最大值」,就是最终的结果。

代码如下:

下面的rob1方法就是例题一。

class Solution {public int rob(int[] nums) {int n = nums.length;return Math.max(nums[0] + rob1(nums,2,n - 2), rob1(nums,1,n - 1));}public int rob1(int[] nums,int left,int right){if(left > right){return 0;}//1.创建dp表//2.初始化//3.填表//4.返回值int n = nums.length;int[] f = new int[n];int[] g = new int[n];f[left] = nums[left];for(int i = left + 1;i <= right;i++){f[i] = g[i - 1] + nums[i];g[i] = Math.max(g[i - 1],f[i - 1]);}return Math.max(f[right],g[right]);}
}

时间复杂度:O(n) 

空间复杂度:O(n)

例题3:删除并获得点数

链接:删除并获得点数

题目简介:

给你一个整数数组 nums ,你可以对它进行一些操作。

每次操作中,选择任意一个 nums[i] ,删除它并获得 nums[i] 的点数。之后,你必须删除 所有 等于 nums[i] - 1 和 nums[i] + 1 的元素。

开始你拥有 0 个点数。返回你能通过这些操作获得的最大点数。

解法(动态规划):

其实这道题依旧是「打家劫舍I」问题的变型。 我们注意到题⽬描述,选择x 数字的时候, x - 1 与x + 1 是不能被选择的。像不像「打家劫舍」问题中,选择i 位置的⾦额之后,就不能选择i - 1 位置(数组中)以及i + 1 位置的⾦额呢~ 因此,我们可以创建⼀个⼤⼩为10001 (根据题⽬的数据范围)的hash 数组,将nums 数 组中每⼀个元素x ,累加到hash 数组下标为x 的位置处,然后在hash数组上来⼀次「打家劫舍」即可。

过程如下图:

弧线表示0到i - 1之间能获得的最大点数。

代码如下:

class Solution {public int deleteAndEarn(int[] nums) {//1.创建dp表//2.初始化//3.填表//4.返回值int n = 10001;int[] arr = new int[n];for(int x:nums){arr[x] += x;}int[] f = new int[n];int[] g = new int[n];f[0] = arr[0];for(int i = 1;i < n;i++){f[i] = g[i - 1] + arr[i];g[i] = Math.max(f[i - 1],g[i - 1]);}return Math.max(f[n - 1],g[n - 1]);}
}

时间复杂度:O(n) 

空间复杂度:O(n)

例题4:粉刷房子

链接:粉刷房⼦

题目简介:

 解法(动态规划):

 1. 状态表示:

对于线性dp ,我们可以⽤「经验+题⽬要求」来定义状态表⽰:但是我们这个题在i 位置的时候,会⾯临「红」「蓝」「绿」三种抉择,所依赖的状态需要细分:

(1)dp[i][0] 表⽰:粉刷到i 位置的时候,最后⼀个位置粉刷上「红⾊」,此时的最⼩花费。 

(2)dp[i][1] 表⽰:粉刷到i 位置的时候,最后⼀个位置粉刷上「蓝⾊」,此时的最⼩花费。

(3)dp[i][2] 表⽰:粉刷到i 位置的时候,最后⼀个位置粉刷上「绿⾊」,此时的最⼩花费。

 2.状态转移方程

因为状态表⽰定义了三个,因此我们的状态转移⽅程也要分析三个:

对于dp[i][0] :

如果第i个位置粉刷上「红⾊」,那么i-1位置上可以是「蓝⾊」或者「绿⾊」。因此我们 需要知道粉刷到i-1位置上的时候,粉刷上「蓝⾊」或者「绿⾊」的最⼩花费,然后加上i 位置的花费即可。于是状态转移⽅程为: dp[i][0] = min(dp[i - 1][1], dp[i - 1][2]) + costs[i - 1][0] ;

同理,我们可以推导出另外两个状态转移⽅程为:

dp[i][1] = min(dp[i - 1][0], dp[i - 1][2]) + costs[i - 1][1] ;

dp[i][2] = min(dp[i - 1][0], dp[i - 1][1]) + costs[i - 1][2] 。

 3.初始化

采用最前⾯加上⼀个「辅助结点」。

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

 4.填表顺序

 5.返回值

根据「状态表⽰」,应该返回最后⼀个位置粉刷上三种颜⾊情况下的最⼩值,因此需要返回: min(dp[n][0], min(dp[n][1], dp[n][2])) 。

代码如下:

class Solution {public int minCost(int[][] costs) {//1.创建dp表//2.初始化//3.填表//4.返回值int n = costs.length;int[][] dp = new int[n + 1][3];for(int i = 1;i <= n;i++){dp[i][0] = Math.min(dp[i - 1][1],dp[i - 1][2]) + costs[i - 1][0];dp[i][1] = Math.min(dp[i - 1][0],dp[i - 1][2]) + costs[i - 1][1];dp[i][2] = Math.min(dp[i - 1][1],dp[i - 1][0]) + costs[i - 1][2];}return Math.min(dp[n][0],Math.min(dp[n][1],dp[n][2]));}
}

时间复杂度:O(n) 

空间复杂度:O(n)

例题5:买卖股票的最佳时机含冷冻

链接:买卖股票的最佳时机含冷冻期

题目简介:

  解法(动态规划):

 1. 状态表示:

这⾥我们选择⽐较常⽤的⽅式,以某个位置为结尾,结合题⽬要求,定义⼀个状态表⽰:由于有「买⼊」「可交易」「冷冻期」三个状态,因此我们可以选择⽤三个数组,其中:

(1)dp[i][0] 表⽰:第i 天结束后,处于「买⼊」状态,此时的最⼤利润。

(2)dp[i][1] 表⽰:第i 天结束后,处于「可交易」状态,此时的最⼤利润。

(3)dp[i][2] 表⽰:第i 天结束后,处于「冷冻期」状态,此时的最⼤利润。

我们要谨记规则:

(1)处于「买⼊」状态的时候,我们现在有股票,此时不能买股票,只能继续持有股票,或者卖 出股票;

(2)处于「买⼊」状态的时候,我们现在有股票,此时不能买股票,只能继续持有股票,或者卖 出股票;

 2.状态转移方程

确定状态表示后,我们可以画图来帮助我们理解根据题目要求我们可以画图下图,并推出状态转移方程。

 3.初始化

三种状态都会⽤到前⼀个位置的值,因此需要初始化每⼀⾏的第⼀个位置:

dp[0][0] :此时要想处于「买⼊」状态,必须把第⼀天的股票买了,因此dp[0][0] = - prices[0] ; dp[0][1] :啥也不⽤⼲即可,因此dp[0][1] = 0 ;

dp[0][2] :手上没有股票,买⼀下卖⼀下就处于冷冻期,此时收益为0 ,因此 dp[0][2] = 0 。

4.填表顺序

根据「状态表⽰」,我们要三个表⼀起填,每⼀个表「从左往右」。

5.返回值

应该返回「卖出状态」下的最⼤值,因此应该返回max(dp[n - 1][1], dp[n - 1] [2]) 。

代码如下:

class Solution {public int maxProfit(int[] prices) {//1.创建dp表//2.初始化//3.填表//4.返回值int n = prices.length;int[][] dp = new int[n][3];dp[0][0] = -prices[0];for(int i = 1;i < n;i++){dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i]);dp[i][1] = Math.max(dp[i - 1][1],dp[i - 1][2]);dp[i][2] = dp[i - 1][0] + prices[i];}return Math.max(dp[n - 1][1],dp[n - 1][2]);}
}

时间复杂度:O(n) 

空间复杂度:O(n)

结语:

其实写博客不仅仅是为了教大家,同时这也有利于我巩固自己的知识点,和一个学习的总结,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进,如果大家有所收获的话还请不要吝啬你们的点赞收藏和关注,这可以激励我写出更加优秀的文章。

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

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

相关文章

Dockerfile(5) - CMD 指令详解

CMD 指定容器默认执行的命令 # exec 形式&#xff0c;推荐 CMD ["executable","param1","param2"] CMD ["可执行命令", "参数1", "参数2"...]# 作为ENTRYPOINT的默认参数 CMD ["param1","param…

DevEco Studio下载与安装(Windows)

下载地址&#xff1a; HUAWEI DevEco Studio和SDK下载和升级 | HarmonyOS开发者 安装时直接点击 next 即可。 运⾏已安装的DevEco Studio&#xff0c;⾸次使⽤&#xff0c;请选择Do not import settings&#xff0c;单击OK。 1.安装Node.js 如果本地有下载&#xff0c;可以…

华为---RSTP(四)---RSTP的保护功能简介和示例配置

目录 1. 技术背景 2. RSTP的保护功能 3. BPDU保护机制原理和配置命令 3.1 BPDU保护机制原理 3.2 BPDU保护机制配置命令 3.3 BPDU保护机制配置步骤 4. 根保护机制原理和配置命令 4.1 根保护机制原理 4.2 根保护机制配置命令 4.3 根保护机制配置步骤 5. 环路保护机…

PHPStudy无法解析php(7.3.4)文件

#告诉服务器&#xff0c;对于以.fcgi、.php或.phtml为后缀的请求&#xff0c;应该使用FPM进行处理。 AddHandler fcgid-script .fcgi .php .phtml #设置了全局默认使用的PHP版本路径 FcgidInitialEnv PHPRC "D:/phpstudy_pro/Extensions/php/php7.3.4nts" #告诉服务器…

十三、Qt多线程与线程安全

一、多线程程序 QThread类提供了管理线程的方法&#xff1a;一个对象管理一个线程一般从QThread继承一个自定义类&#xff0c;重载run函数 1、实现程序 &#xff08;1&#xff09;创建项目&#xff0c;基于QDialog &#xff08;2&#xff09;添加类&#xff0c;修改基于QThr…

“环波罗的海”包围圈将正式形成

据“直新闻”的消息称&#xff0c;近日匈牙利国会同意了瑞典加入北约的申请&#xff0c;在走完相关后续程序后&#xff0c;瑞典就将成为北约第三十二个成员国&#xff0c;而北约对俄罗斯打造的“环波罗的海”包围圈也将正式形成&#xff0c;即除俄方外&#xff0c;波罗的海周边…

遥感影像处理(ENVI+ChatGPT+python+ GEE)处理高光谱及多光谱遥感数据

遥感技术主要通过卫星和飞机从远处观察和测量我们的环境&#xff0c;是理解和监测地球物理、化学和生物系统的基石。ChatGPT是由OpenAI开发的最先进的语言模型&#xff0c;在理解和生成人类语言方面表现出了非凡的能力。本文重点介绍ChatGPT在遥感中的应用&#xff0c;人工智能…

111期_C++_2024年1月份作业博客_选择题错题总结

一、野指针问题&#xff1a;在定义的时候没有初始化&#xff0c;就不能能用scanf 或 printf 二、一个变量出现在表达式的两边作为两个不同的操作数&#xff0c; 并且其中一个操作数带有&#xff0c;此时表达式出现歧义 三、两端出栈问题&#xff1a; 错因&#xff1a;未理解题…

力扣SQL50 进店却未进行过交易的顾客 查询

Problem: 1581. 进店却未进行过交易的顾客 文章目录 思路Code 思路 &#x1f468;‍&#x1f3eb; 山山山林老木 左连接查询筛选 transation_id 为 null 的值group by customer_id Code select v.customer_id ,count(customer_id) count_no_trans from Visits v left jo…

java -进行堆转储文件分析

文章目录 前言java -进行堆转储文件分析1. 首先在window上主动生成堆转储文件2. 获取堆转储文件的方式3. 堆转储文件分析 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人…

【24年最新版PythonPycharm安装】保姆级别安装教学!附激活码插件分享~

Python 下载安装 Python可以编译成可执行文件&#xff08;。 py&#xff09;&#xff0c;并通过网络在计算机和其它终端设备上运行。它有内置的数据类型、函数、类和对象&#xff0c;可以将其用于各种目的&#xff0c;例如管理数据和脚本开发。 Python已经成为一种非常流行的…

PyTorch深度学习快速入门

PyTorch深度学习快速入门 1.PyTorch环境配置及安装2.python编辑器的选择、安装、配置&#xff08;pycharm、JupyTer安装&#xff09;3.为什么torch.cuda.is_available()返回false4.python学习中两大法宝函数&#xff08;也可用在pytorch&#xff09;5.pycharm和jupyter&#xf…

论文设计任务书学习文档|基于Vue.js的库存管理系统的设计与实现

文章目录 论文(设计)题目:基于Vue.js的库存管理系统的设计与实现1、论文(设计)的主要任务及目标2、论文(设计)的主要内容3、论文(设计)的基本要求4、进度安排论文(设计)题目:基于Vue.js的库存管理系统的设计与实现 1、论文(设计)的主要任务及目标 基于Vue.js的…

Android Shadow插件化框架分析与集成(一)

一、shadow源码导入及分析 1、下载项目源码 2、导入到Android studio 3、设置jdk及sdk版本 包/应用描述类型sample-constant公共字符串常量libsample-host宿主应用applicationsample-host-lib宿主应用依赖包libsample-manager是插件管理器的动态实现,主要负责加载插件和安装…

【C++】用文件流的put和get成员函数读写文件

题目 编写一个mycopy程序&#xff0c;实现文件复制的功能。用法是在控制台输入&#xff1a; mycooy 源文件名 目标文件名 参数介绍 m a i n main main 函数的参数有两个&#xff0c;一个int类型参数和一个指针数组。 a r g c argc argc 表示参数的个数。参数为void时 a r g …

跨区域复制建筑UI输入框脚本迷你世界

--复制区域文件 --设置坐标起点&#xff0c;终点 --创建区域 --获取坐标id,data --星空露珠工作室制作 local pos1{x-16,y7,z28} local pos2{x28,y44,z-9} local block{num0} local str{} local str0{} local num0 local count0 local ui6 --几个输入框 local romath.random(…

redis-RedisTemplate.opsForGeo 的geo地理位置及实现附近的人的功能

redis内部使用的是 zset 数据结构存储&#xff0c;如下 import cn.huawei.VideoApplication; import cn.huawei.domain.Jingqu; import cn.huawei.service.JingquService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired…

Typora快捷键设置详细教程(内附每个步骤详细截图)

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

leetcode:860.柠檬水找零

题意&#xff1a;按照支付顺序&#xff0c;进行支付&#xff0c;能够正确找零。 解题思路&#xff1a;贪心策略&#xff1a;针对支付20的客人&#xff0c;优先选择消耗10而不是消耗5&#xff0c;因为5可以用来找零10或20. 代码实现&#xff1a;有三种情况&#xff08;代表三种…

蓝桥杯-灌溉

参考了大佬的解题思路&#xff0c;先遍历一次花园&#xff0c;找到所有的水源坐标&#xff0c;把它们存入 “水源坐标清单” 数组内&#xff0c;再读取数组里的水源坐标进行扩散。 #include <iostream> using namespace std; int main() {int n,m,t,r,c,k,ans0,list_i0;…