动态规划:万变不离其宗,带你吃透股票系列问题

前言:

对于买卖股票问题而言,最关键的是我们对问题的处理方式(对于每一天而言,我们应该描述当天买入卖出还是只描述每天股票的只有或者不持有的状态呢?)我们应该描述每天股票是否持有的状态,因为每天持有股票的状态很好描述,只有持有和不持有这两种状态, 但是如果选择描述在哪一天买入卖出类似这种状态,描述起来就很复杂了

一.买卖股票的最佳时机I(只能买一次)

题目描述(链接:力扣)

解题思路

我们使用动规五部曲对其进行分析:

1.数组下标及含义:在这里我们使用二维数组作为dp数组,为什么要使用二维数组呢?因为哪一天作为一个变量,每一天同时也会有两种状态:持有和不持有股票,因此我们使用dp[n][2]作为dp数组,n代表哪一天,2代表两种不同的状态,dp[][]代表当天某种状态下的最大利润。

2.递推公式:对于当前问题而言,只能买卖一次,对于每一天的状态而言dp[n][0]代表着持有股票:如果持有股票,则存在两种可能性:在前一天就持有,或者在当天买入,我们取其中利润的最大值,所以:dp[n][0]=max(dp[n-1][0],-prices[i]])(由于只能买卖一次,如果当天买入,意味着之前肯定没有买入,所以今天买入所获得的利润为-prices[i]),dp[n][1]代表着当天不持有股票的状态,同样存在两种组成的可能性:一种是由dp[n-1][0]继承而来,即前一天的状态就是未持有,今天的状态是前一天状态的延续,第二种是dp[n-1][0]+prices[i],即昨天是持有股票的状态,而今天把股票出售了,同样是两者取最大值。

3.初始化:我们由递推公式能得出的结论是:我们后一天的状态依赖于前一天的状态,所以第一天dp数组的正确性十分重要,dp[0][0]:代表第一天持有股票,所以其利润为:--prices[0] dp[0][1] 第一天不持有股票,所以其利润为:0

4.遍历顺序:既然是前面的推出后面的,所以遍历顺序自然是从前往后

5.打印dp数组:通过打印dp数组检查最后结果的正确性

代码实现

class Solution {
    public int maxProfit(int[] prices) {
        //排除特殊情况
        if(prices.length==1){
            return 0;
        }
        //创建dp数组
        int[][]dp=new int[prices.length][2];
        dp[0][0]=-prices[0];
        dp[0][1]=0;
        for(int i=1;i<prices.length;++i){
            dp[i][0]=Math.max(dp[i-1][0],-prices[i]);
            dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]+prices[i]);
        }
        return dp[prices.length-1][1];
    }
}

二.买卖股票的最佳时机II(可以买无数次)

题目描述(链接:力扣)

解题思路

我们仍然使用动规五部曲进行分析:

1.数组下标和其代表的含义:每一天同时也会有两种状态:持有和不持有股票,因此我们使用dp[n][2]作为dp数组,n代表哪一天,2代表两种不同的状态,dp[][]代表当天某种状态下的最大利润。

2.递推公式:dp[n][0]代表当天持有股票,则有以下两种可能性:①延续前面的有股票的状态②前一天没有股票,但是今天新买了股票,我们仍然取两者的最大值:dp[n][0]=max(dp[n-1][0],dp[n-1][1]-prices[i]),dp[n][1]代表当天不持有股票,同样有两种可能性:前一天持有但是今天卖掉了,或者延续前一天没有股票的状态,状态方程为:dp[n][1]=max(dp[n-1][1],dp[n-1][1]+prices[i])

3.初始化:第一天持有股票:dp[0][0]=-prices[0] 第一天不持有股票: dp[0][1]=0

4.遍历顺序:与买卖股票最佳时机1一致,从前往后遍历

5.打印:通过打印数组,确认结果的正确性

代码实现

class Solution {
    public int maxProfit(int[] prices) {
        //创建dp数组
        int[][]dp=new int[prices.length][2];
        //初始化
        dp[0][0]=-prices[0];
        //进行遍历
        for(int i=1;i<prices.length;++i){
            dp[i][0]=Math.max(dp[i-1][1]-prices[i],dp[i-1][0]);
            dp[i][1]=Math.max(dp[i-1][0]+prices[i],dp[i-1][1]);
        }
        return dp[prices.length-1][1];
    }
}

三.买卖股票的最佳时机III(只能买两次)

题目描述(链接:力扣)

解题思路

与前两个买卖股票II应用场景有所不同,这个题目对买卖股票添加了限制,限制买卖股票的次数(只允许买卖两次), 看似与买卖股票一类似(买卖股票1只允许买卖一次),但是其场景却变化了很多:买卖股票I与II的区别只是在递推公式中的买入股票时的利润(I是dp[n][0]=max(dp[n-1][0],-prices[i]]),II是:dp[n][0]=max(dp[n-1][0],dp[n-1][1]-prices[i])),但是一旦限制股票只能买卖两次,其场景就复杂起来了,这时需要重新定义1.数组下标及其含义dp数组下标及其本身的含义:dp[n][0]:第n天没有进行任何操作:dp[n][1]:第一次持有股票 dp[n][2]:第一次不持有股票 dp[n][3]:第二次持有股票dp[n][4] 第二次不持有股票。

2.递推公式:每一种状态下的递推公式也有所不同,我们一一进行解释:dp[n][1]=max(dp[n-1][1],dp[n-1][0]-prices[i]) (前一天第一次持有股票的状态或者前一天没有购买股票,今天刚购买),dp[n][2]=max(dp[n-1][2],dp[n-1][1]+prices[i])(延续前一天第一次不持有的状态或者前一天持有,今天抛售)dp[n][3]=max(dp[n-1][3],dp[n-1][2]-prices[i])(延续前一天的第二次持有或者前一天没有持有,今天刚购买)dp[n][4]=max(dp[n-1][4],dp[n-1][3]+prices[i])

3.初始化

dp[0][0]:第一天什么也没有操作,故结果为0 ,dp[0][1] 第一天第一次购买,利润是-prices[0] ,dp[0][2] 第一天出售股票,利润为0  dp[0][3] 第一天进行第二次购买股票,利润为(-prices[0],第一天买入卖出再买入) dp[0][4]第二次进行抛售,利润同样为0

4.遍历顺序:与前面一致,都是从前往后遍历

5.打印查看结果正确性

代码实现

class Solution {
    public int maxProfit(int[] prices) {
        //定义数组
        int[][]dp=new int[prices.length][5];

        //初始化
        dp[0][0]=0;
        dp[0][1]=-prices[0];
        dp[0][2]=0;
        dp[0][3]=-prices[0];
        dp[0][4]=0;
        //进行遍历
        for(int i=1;i<prices.length;++i){
            dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]-prices[i]);
            dp[i][2]=Math.max(dp[i-1][2],dp[i-1][1]+prices[i]);
            dp[i][3]=Math.max(dp[i-1][3],dp[i-1][2]-prices[i]);
            dp[i][4]=Math.max(dp[i-1][4],dp[i-1][3]+prices[i]);
        }
        return dp[prices.length-1][4];
    }
}

四.买卖股票的最佳时机IV(只能买k次)

题目描述(链接:力扣)

解题思路

解题思路与买卖股票III类似了:买卖股票III加上什么也不做的状态是定义了5种状态(未操作、第一次买入、第一次卖出、第二次买、第二次卖出),所以买卖股票IV相对于III而言,只有一点需要变化,那就是每一天的状态增加了:所以我们采用循环的方式,如下:其中k代表最多允许买卖的次数

for(int j=0;j<2*k-1;j+=2){
                dp[i][j+1]=Math.max(dp[i-1][j+1],dp[i-1][j]-prices[i]);
                dp[i][j+2]=Math.max(dp[i-1][j+2],dp[i-1][j+1]+prices[i]);
            }

我们直接给出完整的代码(与买卖股票III类似)

class Solution {
    public int maxProfit(int k, int[] prices) {
        //创建数组
        int[][]dp=new int[prices.length][2*k+1];
        //初始化
        for(int i=0;i<2*k;i+=2){
            dp[0][i+1]=-prices[0];
        }
        //遍历
        for(int i=1;i<prices.length;++i){
            for(int j=0;j<2*k-1;j+=2){
                dp[i][j+1]=Math.max(dp[i-1][j+1],dp[i-1][j]-prices[i]);
                dp[i][j+2]=Math.max(dp[i-1][j+2],dp[i-1][j+1]+prices[i]);
            }
        }
        return dp[prices.length-1][2*k];
    }
}

五.买卖股票的最佳时机V(含冷冻期)

题目描述(链接:力扣)

解题思路

与前面题目描述有所不同的是,这次的股票问题存在一个冷冻期,也正是这个冷冻期的存在,我们dp数组对应的下标及dp数组的含义都要发生一定的变化:我们之前只是将每一天的状态分为持有股票和不持有股票两种(不持有股票包含了今天刚卖掉股票和延续之前卖掉股票的状态乳癌),但是一旦引入冷冻期这个概念,我们就必须将卖出股票的状态进行细分:1.今天刚卖出股票2.处于冷冻期内(昨天卖出股票)3.之前卖出股票且不处于冷冻期内,所以总共的状态为4种,在此基础上加上持有股票的状态,使用动规五部曲对此进行分析:

①dp数组下标含义及其数组含义dp[i][0]:代表当前持有股票 dp[i][1]:代表当前保持卖出股票(最晚是前天卖出股票);dp[i][2]代表今天刚卖出股票;dp[i][3]代表当前正处于冷冻期内

②递推公式:dp[i][0]=max(d[i-1][0]max(dp[i-1][3]-prices[i],dp[i-1][1]-prices[i]));(今天手中有股票,可能是延续前一天手中有股票的状态和昨天处于冷冻期内,今天买股票,或者昨天保持卖出股票的状态,今天买入股票) ;dp[i][1]=max(dp[i-1][3],dp[i-1][1])(保持前面的卖出股票状态或者昨天处于冷冻期,今天进入保持卖出股票) dp[i][2]=d[i][0]+prices[i](今天刚卖出股票,意味着前一天手里一定有股票,所以dp[i][2]只和前一天的dp[i-1][0]有关);dp[i][3]=dp[i-1][2](今天处于冷冻期内意味着一定是昨天刚卖出股票,所以只由昨天的dp[i-1][2]决定)

③初始化:dp[0][0]=-prices[0](今天刚买股票) dp[0][1]=0(在第0天存在这个状态实际上是不合法的,我们可以将其理解为今天手中没有股票,所以初始化为0) dp[0][2]=0(今天买了股票之后直接买卖出了,所以利润为0) dp[0][3]=0(今天处于冷冻期的状态也是不合法的,我们将其初始化为0)

④遍历顺序:依然是从前面推出后面,所以遍历顺序也是从前往后

⑤ 打印数组:通过打印dp数组判断合法性

代码实现

class Solution {
    public int maxProfit(int[] prices) {
        //创建数组
        int[][]dp=new int[prices.length][4];
        //进行初始化
        dp[0][0]=-prices[0];//0代表持有股票
        dp[0][1]=0;//1代表保持卖出股票
        dp[0][2]=0;//2表示卖出股票
        dp[0][3]=0;//3表示在冷冻期内
        //进行遍历循环
        for(int i=1;i<prices.length;++i){
         dp[i][0]=Math.max(dp[i-1][0],Math.max(dp[i-1][3]-prices[i],dp[i-1][1]-prices[i]));
         dp[i][1]=Math.max(dp[i-1][1],dp[i-1][3]);
         dp[i][2]=dp[i-1][0]+prices[i];
         dp[i][3]=dp[i-1][2];
        }
        //返回最后卖出股票中的最大值
        return Math.max(dp[prices.length-1][1],Math.max(dp[prices.length-1][2],dp[prices.length-1][3]));
    }
}

六.买卖股票的最佳时机VI(含手续费)

题目描述(链接:力扣)

解题思路

本题与买卖股票II基本一致,唯一的区别在于我们在卖出股票的时候需要加上手续费,所以体现在代码上就是递推公式发生了变化:

dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee);

其他与买卖股票II完全一致:我们直接给出代码. 

代码实现

class Solution {
    public int maxProfit(int[] prices, int fee) {
        //定义dp数组
        int[][]dp=new int[prices.length][2];
        //初始化
        dp[0][0]=-prices[0];
        dp[0][1]=0;
        //进行遍历
        for(int i=1;i<prices.length;++i){
            dp[i][0]=Math.max(dp[i-1][1]-prices[i],dp[i-1][0]);
            dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]+prices[i]-fee);
        }
        return dp[prices.length-1][1];
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【论文阅读】基于人工智能目标检测与跟踪技术的过冷流沸腾气泡特征提取

Bubble feature extraction in subcooled flow boiling using AI-based object detection and tracking techniques 基于人工智能目标检测与跟踪技术的过冷流沸腾气泡特征提取 期刊信息&#xff1a;International Journal of Heat and Mass Transfer 2024 级别&#xff1a;EI检…

等保测评与商用密码共铸工控安全“双评合规”新篇章

最近听说了一个段子&#xff1a;“网络安全就像美女的内衣&#xff0c;等保和密评就是最贴身的内衣两件套&#xff0c;上下身一件都不能少。否则你的魔鬼身材&#xff08;核心数据&#xff09;就有可能被色狼&#xff08;黑客&#xff09;一览无余&#xff08;数据泄漏&#xf…

将SU模型导入ARCGIS,并获取高度信息,多面体转SHP文件(ARCMAP)

问题:将Sketchup中导出的su模型,导入arcgis并得到面shp文件,进而获取各建筑的高度、面积等信息。 思路: (1)导入arcgis得到多面体 (2)转为面shp文件 (3)计算高度/面积等 1、【3D Analyst工具】【转换】【由文件转出】【导入3D文件】(在此步骤之间,建议先建立一个…

学习磁盘管理

文章目录 一、磁盘接口类型二、磁盘设备的命名三、fdisk分区四、自动挂载五、扩容swap六、GPT分区七、逻辑卷管理八、磁盘配额九、RAID十、软硬链接 一、磁盘接口类型 IDE、SATA、SCSI、SAS、FC&#xff08;光纤通道&#xff09; IDE, 该接口是并口。SATA, 该接口是串口。SCS…

linux系统---nginx(2)rewrite重写功能

目录 一、rewrite概述 1、rewrite功能 2、跳转场景 二、标准配置指令 1、rewrite日志记录指令 2、未初始化变量告警日志记录指令 3、rewrite 指令 3.1 正则表达式 三、rewrite模块使用实例 1.基于域名的跳转 一、rewrite概述 1、rewrite功能 访问重写 rewrite 是 …

分布式事务(7)之Seata简介

一、分布式事务解决方案 2PC即两阶段提交协议&#xff0c;是将整个事务流程分为两个阶段&#xff0c;准备阶段&#xff08;Prepare phase&#xff09;、提交阶段&#xff08;commit phase&#xff09;&#xff0c;2是指两个阶段&#xff0c;P是指准备阶段&#xff0c;C是指提交…

C# 发现同一依赖程序集的不同版本间存在冲突。请将项目文件中的“AutoGenerateBindingRedirects”属性设置为 true

C# 发现同一依赖程序集的不同版本间存在冲突。请将项目文件中的“AutoGenerateBindingRedirects”属性设置为 true Severity Code Description Project File Line Suppression State Warning Found conflicts between different versions of the same dependent assembly. P…

C#区域医院云LIS信息管理系统源码 标本管理、两癌筛查、数据分析、试剂管理

目录 ​编辑 区域医院云LIS系统功能亮点&#xff1a; 云LIS系统功能&#xff1a; 一、 基础管理 二、 前处理&#xff08;实验室&#xff09; 三、 标本处理 四、 样本检验 五、 统计报表 六、 质控管理 七、 基本工作流程 区域LIS系统特点&#xff1…

使用logicflow流程图实例

一.背景 需要使用流程引擎开发项目&#xff0c;没有使用flowable、activiti这类的国外流程引擎&#xff0c;想使用国内的引擎二次开发&#xff0c;缺少单例模式的流程画图程序&#xff0c;都是vue、react、angluer的不适合&#xff0c;从网上找了antx6、logicflow、bpmn.js。感…

顺序表的列题(力扣)和旋转数组

文章目录 一.删除有序数组中的重复项&#xff08;取自力扣&#xff09; 二.合并两个有序数组&#xff08;取自力扣&#xff09; 三.旋转数组&#xff08;多解法&#xff09; 前言 见面我们说到了顺序表今天来分享几个有关于顺序表的题目 一.删除有序数组中的重复项&#xff…

The authenticity of host ‘github.com (20.205.243.166)‘ can‘t be established.

1、运行git clone报错&#xff1a; The authenticity of host github.com (20.205.243.166) cant be established. ECDSA key fingerprint is SHA256:p2QAC1TJYererOttrVc98/R1BWERWu3/LiyFdHfQM. Are you sure you want to continue connecting (yes/no/[fingerprint])? 这个…

cmake 构建Qt存在多个子项目的应用

概述&#xff1a;一般在开发UI应用时候我们都会存在多个子项目&#xff0c;比如一个是主UI界面的项目&#xff0c;有些动态库的项目&#xff0c;主UI中用到子项目中的动态库&#xff0c;我们来看看如何利用cmake来构建这样的一个工程&#xff0c;方便我们在跨平台中开发(macos、…

【HarmonyOS】鸿蒙开发之Video组件——第4.2章

Video组件内VideoOptions属性简介 src&#xff1a;设置视频地址。currentProgressRate&#xff1a;设置视频播放倍速&#xff0c;参数说明如下&#xff1a; number|string&#xff1a;只支持 0.75 &#xff0c; 1.0 &#xff0c; 1.25 &#xff0c; 1.75 &#xff0c; 2.0 。P…

智慧物流之道:数据可视化引领全局监控

在智慧物流的背景下&#xff0c;数据可视化催生了物流管理的全新范式。首先&#xff0c;通过数据可视化&#xff0c;物流企业可以实现对整个供应链的全景式监控。下面我就可以可视化从业者的角度&#xff0c;简单聊聊这个话题。 首先&#xff0c;图表和地图的直观展示使决策者能…

Golang使用Swag搭建api文档

1. 简介 Gin是Golang目前最为常用的Web框架之一。 公司项目验收需要API接口设计说明书&#xff08;Golang后端服务基于Gin框架编写&#xff09;&#xff0c;编写任务自然就落到了我们研发人员身上。 项目经理提供了文档模板&#xff0c;让我们参考模板来手动编写&#xff0c;要…

教机械臂搭积木?《多Agent系统引论》第4章 实用推理Agent 小结

4.0 前言 Agent起作用&#xff0c;不仅仅是逻辑推理的一种、一个过程&#xff0c;还有其他过程在起作用。为了建立贴合实际的Agent&#xff0c;我们需要提出一种新的概念的模型。这就是实用推理型Agent。 4.1 推理分两步 这种Agent把推理的过程分为了两步&#xff0c;一步是理…

Nginx重写功能和反向代理

目录 一、重写功能rewrite 1. ngx_http_rewrite_module模块指令 1.1 if 指令 1.2 return 指令 1.3 set 指令 1.4 break 指令 2. rewrite 指令 3. 防盗链 3.1 实现盗链 3.2 实现防盗链 4. 实用网址 二、反向代理 1. 概述 2. 相关概念 3. 反向代理模块 4. 参数配…

鸿蒙开发-UI-图形-绘制自定义图形

鸿蒙开发-UI-组件 鸿蒙开发-UI-组件2 鸿蒙开发-UI-组件3 鸿蒙开发-UI-气泡/菜单 鸿蒙开发-UI-页面路由 鸿蒙开发-UI-组件导航-Navigation 鸿蒙开发-UI-组件导航-Tabs 鸿蒙开发-UI-图形-图片 鸿蒙开发-UI-图形-绘制几何图形 文章目录 前言 一、使用画布组件绘制自定义图形 1.初…

Linux进程间通信(2)

目录 前言&#xff1a; 正文 1.命名管道 1.1创建及使用 1.2命名管道的工作原理 1.3命名管道与匿名管道的区别 2.命名管道特点及应用场景 2.1特点 2.2场景 3.命名管道实操 3.1实现文件拷贝 3.2实现进程控制 总结&#xff1a; 前言&#xff1a; 管道中除了…

解锁财务信任,掌握企业业务合作中的倾听艺术

企业在经营管理过程中&#xff0c;经常会思考如何才能成为一个完美的财务业务融合体&#xff0c;实现业务合作的最大价值。当我们置身于企业战略规划的构建过程中&#xff0c;就会明显的感觉到&#xff0c;获得财务信任有助于指导团队做出重大决策并推动企业未来的行动。市场和…