当前位置: 首页 > news >正文

一篇文章带你用动态规划解决股票购买时机问题

动态规划的解题步骤可以分为以下五步,大家先好好记住

1.创建dp数组以及明确dp数组下标的含义  2.制定递推公式  3.初始化  4.遍历顺序  5.验证结果


 股票购买时机问题的解题核心思路

当天的收益是根据前一天持有股票还是不持有股票的状态决定的

那么很自然的我们就想到了使用动态规划的思想来解决问题,接下来就根据动态规划以及解题的核心思想来解决 股票购买时机问题 


121. 买卖股票的最佳时机

根据题意: 某一天 买入这只股票,并选择在 未来的某一个不同的日子 

也就是说我们只用买卖股票一次即可 那么根据动态规划的解题步骤以及核心思想我们一步步分析

1.创建dp数组以及明确dp数组下标的含义

//0 表示持不有股票的状态 1表示持有股票的状态
int[][] dp = new int[prices.length][2];

2.制定递推公式

//持有的状态 - 要么是保持持有状态 要么是今天买入股票
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]);

3.初始化

dp[0][0] = -prices[0];
dp[0][1] = 0;

4.遍历顺序

//从前往后遍历- 由于我们是从 i = 1 开始遍历的所以是prices[i-1]
for(int i = 1;i < dp.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]);
}

5.验证

 

最后贴上完整代码

    public int maxProfit(int[] prices) {int len = prices.length;//dp数组下标的含义是当日持有股票和不持有股票的最大利润int[][] dp = new int[len][2];//递推公式//dp[i][0] 表示持有股票 dp[i][1] 表示不持有股票//dp[i][0] = Math.max(dp[i-1][0],dp[i-1][0] - prices[i]);//dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0] + prices[i]);//初始化dp[0][0] = -prices[0];dp[0][1] = 0;for(int i = 1;i < dp.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 Math.max(dp[len-1][0],dp[len-1][1]);}

122. 买卖股票的最佳时机 II

本题遇上一题最大的不同就是可以多次买卖股票了,大题思路和上一题是差不多的

所以我们只需要在 2.指定递推公式 这里修改一下即可

由于可以多次买卖股票了,那么在今天买入股票这个步骤就需要参考昨天不持有股票的状态了

这就是和上一题不同的地方,上一题由于只需要买入一次,所以不用参考任何状态

那么只需要简单的进行修改即可

for(int i = 1;i < dp.length;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][0] + prices[i]);}

123. 买卖股票的最佳时机 III 

本题说了只能购买两次股票那么难度就增加了,但是核心思路“当天的收益是根据是前一天持有股票还是不持有股票的状态决定的”还是不变的

1.创建dp数组以及明确dp数组下标的含义 

int len = prices.length;
//dp数组的下标的含义是当日持有股票或者不持有股票获得的最大收益 
//0~1表示第一次持有股票 2~3表示第二次持有股票
int[][] dp = new int[len + 1][4];

2.制定递推公式 

第一次持有是第一题的递推公式思路(只能买卖股票一次)

第二次持有是第二题的思路,因为此时买卖股票需要结合上一次买卖股票的状态来决定(多次买卖股票)

结合上两题的思想我们得到递推公式

//第一次持有
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]);
//第二次持有
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]);

3.初始化 

//第一次持有
dp[0][0] = -prices[0];
//第一次不持有
dp[0][1] = 0;
//第二次持有
dp[0][2] = -prices[0];
//第二次不持有
dp[0][3] = 0;

4.遍历顺序 

从前往后

        
for(int i = 1;i < dp.length;i++){dp[i][0] = Math.max(dp[i-1][0], -prices[i-1]);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]);
}

最后完整代码如下

    public int maxProfit(int[] prices) {int len = prices.length;int[][] dp = new int[len][4];//第一次持有dp[0][0] = -prices[0];//第一次不持有dp[0][1] = 0;//第二次持有dp[0][2] = -prices[0];//第二次不持有dp[0][3] = 0;for(int i = 1;i < dp.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]);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]);}return dp[len-1][3];}

188. 买卖股票的最佳时机 IV

别被吓着!这题实际上和上一题是一模一样的! 

区别就在于这一题是购买K次,而K则是题目随机分配的

但思路还是一样的!

那么怎么来解决掉0~K次的各个状态呢?答案也很简单!使用for循环

比如初始化的时候我们可以使用for循环来进行初始化

        //初始化for(int i = 1;i < k*2 + 1;i += 2){//规定奇数是持有的状态 偶数是不持有的状态dp[0][i] = -prices[0];}

 当我们制定递推公式的时候也是使用for循环来帮助我们实现的

        //遍历顺序for(int i = 1;i < len;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]);}}

你们看完可能会觉得很疑惑:不是说第一次持有股票不需要参考任何状态吗?那这个代码怎么解释

dp[i - 1][j] - prices[i];

 注意我们在初始化的时候是从 i = 1 开始的 也就是说 第一次持有股票时的状态还是

dp[i][0] = - prices[i];

这样大家应该明白了吧

最后贴上完整的代码

class Solution {public int maxProfit(int k, int[] prices) {int len = prices.length;//dp数组int[][] dp = new int[len][k*2 + 1];//递推公式//持有操作 奇数//dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j] - price[i]);//不持有操作 偶数//dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j] + price[i]);//初始化for(int i = 1;i < k*2 + 1;i += 2){dp[0][i] = -prices[0];}//遍历顺序for(int i = 1;i < len;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[len - 1][k*2];}
}

309. 买卖股票的最佳时机含冷冻期(老实说,这题是我觉得股票买卖时机里面最难的一题...)

 想要解决这道难题首先我们得分析好状态,不然会越来越懵逼的

首先由持有状态 不持有状态(保持不持有状态,前一天是冷冻期状态)  冷冻期状态 

我们先定义好dp数组

        int[][] dp = new int[len][4];//1.持有股票(保持持有股票/今天买入股票) 2.保持不持有股票 3.今天卖出股票 4.处于冷冻期//持有dp[0][0] = -prices[0];//不持有dp[0][1] = 0;//今天卖出股票dp[0][2] = 0;//处于冷冻期 = 昨天卖出股票dp[0][3] = dp[0][2];

持有状态可以由不持有状态和冷冻期状态进行推导,它可以是保持持有状态也可以是今天买入

dp[i][0] = Math.max(Math.max(dp[i-1][1],dp[i-1][3]) - prices[i],dp[i-1][0]);

那么不持有状态可以由保持不持有状态和前一天是冷冻期进行推导

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];

状态推导完了剩下也就简单了,直接上代码

class Solution {public int maxProfit(int[] prices) {int len = prices.length;int[][] dp = new int[len][4];//1.持有股票(保持持有股票/今天买入股票) 2.保持不持有股票 3.今天卖出股票 4.处于冷冻期//持有dp[0][0] = -prices[0];//不持有dp[0][1] = 0;//今天卖出股票dp[0][2] = 0;//处于冷冻期 = 昨天卖出股票dp[0][3] = dp[0][2];for(int i = 1;i < len;i++){//持有股票dp[i][0] = Math.max(Math.max(dp[i-1][1],dp[i-1][3]) - prices[i],dp[i-1][0]);//保持不持有股票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[len-1][1],Math.max(dp[len-1][2],dp[len-1][3]));}   
}

714. 买卖股票的最佳时机含手续费

本题实际上只用在第二题的基础上扣除手续费就好了只需要修改这一段代码即可

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

股票买卖时机问题我们只需要把握一点就是把 持有和不持有 两个状态分析好,题目就会变得很简单

相关文章:

一篇文章带你用动态规划解决股票购买时机问题

动态规划的解题步骤可以分为以下五步&#xff0c;大家先好好记住 1.创建dp数组以及明确dp数组下标的含义 2.制定递推公式 3.初始化 4.遍历顺序 5.验证结果 股票购买时机问题的解题核心思路 当天的收益是根据前一天持有股票还是不持有股票的状态决定的 那么很自然的我们就想…...

【设计模式】使用建造者模式组装对象并加入自定义校验

文章目录 1.前言1.1.创建对象时的痛点 2.建造者模式2.1 被建造类准备2.2.建造者类实现2.3.构建对象测试2.4.使用lombok简化建造者2.5.lombok简化建造者的缺陷 3.总结 1.前言 在我刚入行不久的时候就听说过建造者模式这种设计模式&#xff0c;当时只知道是用来组装对象&#xf…...

简单聊聊低代码

在数字经济迅速发展的背景下&#xff0c;越来越多的企业开始建立健全业务系统、应用、借助数字化工具提升管理效率&#xff0c;驱动业务发展&#xff0c;促进业绩增长。在这一过程中&#xff0c;和许多新技术一样&#xff0c;低代码&#xff08;Low-code&#xff09;开发被推上…...

SystemVerilog Assertions应用指南 第一章(1.27章节 “within”运算符)

“ within”构造允许在一个序列中定义另一个序列。 seq1 within seq2 这表示seq1在seq2的开始到结束的范围内发生,且序列seq2的开始匹配点必须在seq1的开始匹配点之前发生,序列seq1的结束匹配点必须在seq2的结束匹配点之前结束。属性p32检查序列s32a在信号“ start”的上升沿和…...

2023年09月 C/C++(七级)真题解析#中国电子学会#全国青少年软件编程等级考试

C/C++编程(1~8级)全部真题・点这里 Python编程(1~6级)全部真题・点这里 第1题:红与黑 有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。 时间限…...

[Mono Depth/3DOD]单目3D检测基础

1. 数据增强 图像放缩和裁剪后&#xff0c;相机内参要做相应变化 import random def random_scale(image, calib, scale_range(0.8, 1.2)):scale random.uniform(*scale_range)width, height image.sizeimage image.resize((int(width * scale), int(height * scale)))cal…...

【Docker 内核详解】namespace 资源隔离(三):PID namespace

namespace 资源隔离&#xff08;三&#xff09;&#xff1a;PID namespace 1.PID namespace 中的 init 进程2.信号与 init 进程3.挂载 proc 文件系统4.unshare() 和 setns() PID namespace 隔离非常实用&#xff0c;它对进程 PID 重新标号&#xff0c;即两个不同 namespace 下的…...

1600*C. Game On Leaves(博弈游戏树)

Problem - 1363C - Codeforces 解析&#xff1a; 我们将目标结点 x 当作树的根&#xff0c;显然&#xff0c;到当 x 的度为 1 的时候&#xff0c;此时行动的人胜利。 我们假设现在的情况为&#xff0c;只剩余三个点&#xff0c;再选择任意一个点&#xff0c;则对方获胜。但是两…...

Apache Ant的安装

介绍 Apache Ant是一个Java库和一个 命令行工具&#xff0c;可以用来构建Java应用。Ant提供了许多内置的任务&#xff08;tasks&#xff09;&#xff0c;可以编译、组装、测试、运行Java应用。Ant也可以构建非Java应用&#xff0c;例如C、C应用。 Ant非常灵活&#xff0c;没有…...

考研:数学二例题--∞−∞和0⋅∞型极限

前言 本文只是例题&#xff0c;建议先参考具体如何做这类型例题。请到主文章中参考&#xff1a;https://blog.csdn.net/grd_java/article/details/132246630 ∞ − ∞ 和 0 ⋅ ∞ \infin - \infin 和 0\infin ∞−∞和0⋅∞ 例题 例1&#xff1a; lim ⁡ x → ∞ x 2 x 2 −…...

C++算法:图中的最短环

题目 现有一个含 n 个顶点的 双向 图&#xff0c;每个顶点按从 0 到 n - 1 标记。图中的边由二维整数数组 edges 表示&#xff0c;其中 edges[i] [ui, vi] 表示顶点 ui 和 vi 之间存在一条边。每对顶点最多通过一条边连接&#xff0c;并且不存在与自身相连的顶点。 返回图中 …...

C++学习——类其实也是一种作用域

以下内容源于C语言中文网的学习与整理&#xff0c;非原创&#xff0c;如有侵权请告知删除。 其实也是一种作用域&#xff0c;每个类都会定义它自己的作用域。在类的作用域之外&#xff0c;普通的成员只能通过对象&#xff08;可以是对象本身&#xff0c;也可以是对象指针或对象…...

Seata入门系列【4】undo_log、global_table、branch_table、lock_table字段及作用详解

1 客户端 1.1 undo_log 在AT模式中&#xff0c;需要在参与全局事务的数据库中&#xff0c;添加一个undo_log表&#xff0c;建表语句如下&#xff1a; SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0;-- ---------------------------- -- Table structure for undo_log -- --…...

虚幻引擎:数据表格的C++常用API

1.将数据表格中的所有数据存到一个数组中 //参数1 // 错误提示 //参数2 // 存储的数组 TArray<FKeyInfoHeader*> array; KeyInfoDT->GetAllRows<FKeyInfoHeader>(TEXT("错误"),array); 2.获取表格中所有的行名称 TArray<FName>array; …...

Java日期格式化(DateFormat类和SimpleDateFormat类)

格式化日期表示将日期/时间格式转换为预先定义的日期/时间格式。例如将日期“Fri May 18 15:46:24 CST2016” 格式转换为 “2016-5-18 15:46:24 星期五”的格式。 在 java 中&#xff0c;可以使用 DateFormat 类和 SimpleDateFormat 类来格式化日期&#xff0c;下面详细介绍这两…...

centos 7 lamp owncloud

OwnCloud是一款开源的云存储软件&#xff0c;基于PHP的自建网盘。基本上是私人使用&#xff0c;没有用户注册功能&#xff0c;但是有用户添加功能&#xff0c;你可以无限制地添加用户&#xff0c;OwnCloud支持多个平台&#xff08;windows&#xff0c;MAC&#xff0c;Android&a…...

屏幕亮度调节保护您的眼睛

官方下载地址&#xff1a; 安果移动 视频演示&#xff1a;屏幕亮度调节-保护您的眼睛_哔哩哔哩_bilibili 嗨&#xff0c;亲爱的用户&#xff0c;你是否有过这样的体验&#xff1a;夜晚安静的时刻&#xff0c;想要在抖音上看看热门的舞蹈、在快手上发现生活的 趣味、或是在哔…...

CentOS Linux下CMake二进制文件安装并使用Visual Studio调试

cmake安装——二进制安装(很简单&#xff0c;推荐&#xff01;&#xff01;) 1&#xff09;下载二进制包。首先就是官网下载二进制安装包(我们是64位系统&#xff0c;就下载对应的包)&#xff0c;这里。 例如&#xff1a;在/home/DOWNLOAD目录下执行&#xff0c;即下载二进制…...

ASP.net相关目录,相关配置文件和.后缀名解释

App_Data&#xff1a;用于存储应用程序的数据文件&#xff0c;例如数据库文件或其他本地文件。 App_Start&#xff1a;包含应用程序的启动配置文件&#xff0c;例如路由配置、日志配置等。 Content&#xff1a;存放应用程序的静态资源文件&#xff0c;如 CSS、JavaScript、图…...

一键批量转换,轻松将TS视频转为MP4视频,实现更广泛的播放和分享!

在享受精彩视频内容的同时&#xff0c;有时我们可能会面临一个问题&#xff1a;某些视频格式可能不太适合我们的播放设备或分享平台。特别是TS格式的视频&#xff0c;在一些情况下可能无法直接播放或上传。但是不用担心&#xff0c;因为我们为您提供了一款强大的视频剪辑工具&a…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

Spring Boot + MyBatis 集成支付宝支付流程

Spring Boot MyBatis 集成支付宝支付流程 核心流程 商户系统生成订单调用支付宝创建预支付订单用户跳转支付宝完成支付支付宝异步通知支付结果商户处理支付结果更新订单状态支付宝同步跳转回商户页面 代码实现示例&#xff08;电脑网站支付&#xff09; 1. 添加依赖 <!…...

英国云服务器上安装宝塔面板(BT Panel)

在英国云服务器上安装宝塔面板&#xff08;BT Panel&#xff09; 是完全可行的&#xff0c;尤其适合需要远程管理Linux服务器、快速部署网站、数据库、FTP、SSL证书等服务的用户。宝塔面板以其可视化操作界面和强大的功能广受国内用户欢迎&#xff0c;虽然官方主要面向中国大陆…...