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

news/2024/4/27 14:53:17/文章来源:https://blog.csdn.net/weixin_43894455/article/details/130372449

188. 买卖股票的最佳时机 IV(困难)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

思路

状态定义

一、首先确定要一天会有几种状态,不难想到有四种:

  • a.当天买入了股票;
  • b.当天卖出了股票;
  • c.当天没有操作,但是之前是买入股票的状态;
  • d.当天没有操作,但是之前是卖出股票的状态;

同时操作四种状态有些复杂,通过分析,我们可以优化成两种状态:「当天持有股票」和「当天不持有股票」。这两种状态分别对应为 a/c 和 b/d 。

二、接下来考虑如何表示这两种状态

  • 最常见的方法是使用 dp[0] 和 dp[1] 来表示,但是这种方式不仅不方便识别数组,而且还增加了 dp数组的一个维度,就本题而言,这种定义方式会得到一个三维数组,难以操作。
  • 所以建议直接使用能明确表示含义的两个dp数组,比如用 buy 表示持有股票的状态, sell 表示不持有股票的状态。

三、最后确定dp数组的维度

  • 首先必须要有表示天数 i 的维度;
  • 还需要有表示买卖次数 k 的维度 j;

四、综上,得到了两个二维的dp数组

  • buy[i][j] 表示到第 i 天为止,恰好进行 j 笔交易,并且当前处于持有股票的状态,在这种情况下的最大利润;
  • sell[i][j] 表示到第 i 天为止,恰好进行 j 笔交易,并且当前处于不持有股票的状态,在这种情况下的最大利润;

确定递推公式

在确定递推公式的时候,需要明确一个概念: k什么时候发生变化。这道题默认一买一卖才算是一次完整的交易,所以只有在卖股票的情况下,k才会发生变化。

确定完k的变化后,递推公式就很容易得到,每种状态都由两种情况组合得到:

  1. buy[i][j]
    • c.第 i-1 天就持有股票,那么当天收益不变,所得现金就是昨天持有股票的收益:buy[i-1][j]
    • a.第 i 天买入股票,所得现金就是昨天不持有股票的收益扣去今天的股票价格:sell[i-1][j] - prices[i]
    • 综合上述两种情况,最大收益就是 max(buy[i-1][j], sell[i-1][j] - prices[i])
  2. sell[i][j]
    • d. 第 i-1 天就不持有股票,那么当天收益不变,所得现金就是昨天不持有股票的收益:sell[i-1][j]
    • b. 第 i 天售出股票,那么所得现金就是昨天持有股票的收益加上今天的股票价格:buy[i-1][j-1] + prices[i]
    • 综合上述两种情况,最大收益就是 max(sell[i-1][j], buy[i-1][j-1] + prices[i])

根据我们对 k 的变化的定义,体现在递推公式中就是在推导 sell[i][j] 的第二种情况时用到的 buy[i-1][j-1] ,因为在卖出股票的时候,就完成了一次交易,因此k的次数加一,所以上一个持有股票的状态就是 j-1 。

dp数组的初始化

  1. 将所有的 buy[0][0...k]sell[0][0...k] 都设置为边界;

  2. 对于 buy[0][0...k] ,由于对应 i = 0 ,因此只有 prices[0] 唯一的股价,所以如果要持有股票,只能选择买入,因此对于 k = 0, buy[0][0] = - prices[0]。由于只有一天,不可能完成一次完整的交易(即不可能售出),所以对于任意的 k >= 1 ,将所有的 buy[0][1...k] 设置一个非常小的值(这里使用 INT_MIN / 2) ,表示不合法的状态。

  3. 同理,对于 sell[0][0...k],对应于 i=0 ,只有 prices[0] ,所以如果要不持有股票,只能不进行任何操作,所以对于 k=0,sell[0][0] = 0,所以对于任意的 k >= 1 ,同样是不合法状态,将所有的 buy[0][1...k] 设置一个非常小的值(这里使用 INT_MIN / 2) 。

最终的返回结果

在遍历完所有的 prices 后,手上不持有股票对应的最大力利润一定严格大于手上持有股票对应的最大利润,然而完成的交易数不是越多越好(比如数组 prices 单调递减,此时不进行任何交易才是最优解),因此最终答案是 sell[n-1][0...k] 的最大值,即 return *max_element(sell[n-1].begin(), sell[n-1].end());

易错点

一、k 的范围:

由于一次完整的交易需要两天,如果交易次数 k 大于 n/2 ,则必然有一天交易了两次,这是没有意义的,因此我们可以减小 k 的值,再进行动态规划,即 k = min(k, n/2);

二、buy 数组的维数:

第二个维度是 k+1 维,表示 k 的范围从 0~k 。如果数组单调递减,此时不进行任何交易是最优解, k=0;

三、j 的循环:

由于 sell[i][j] 的状态转移方程中包含 buy[i-1][j-1] ,如果 j=0 时,这是一个不合法状态,所以无需对 sell[i][j] 进行状态转移,让它保持值为 0 即可,所以在 j=0 时,只需要对 buy[i][0] 单独处理。

代码

class Solution {
public:int maxProfit(int k, vector<int>& prices) {int n = prices.size();if(n == 0)  return 0;vector<vector<int>> buy(n, vector<int>(k+1));vector<vector<int>> sell(n, vector<int>(k+1));// k值的预处理k = min(k, n/2);// 预处理buy[0][0] = -prices[0];sell[0][0] = 0;for(int i=1; i<=k; ++i){// 不能设置为 INT_MINbuy[0][i] = sell[0][i] = INT_MIN / 2;}for(int i=1; i<n; ++i){buy[i][0] = max(buy[i-1][0], sell[i-1][0] - prices[i]);for(int j=1; j<=k; ++j){buy[i][j] = max(buy[i-1][j], sell[i-1][j] - prices[i]);sell[i][j] = max(sell[i-1][j], buy[i-1][j-1] + prices[i]);}}return *max_element(sell[n-1].begin(), sell[n-1].end());}
};

空间压缩

从上述代码不难发现,当前持有股票和当前不持有股票的最大收益都只和前一个状态有关,buy[i][j] = max(buy[i-1][j], sell[i-1][j] - prices[i]); sell[i][j] = max(sell[i-1][j], buy[i-1][j-1] + prices[i]); ,因此可以对两个数组进行空间压缩,只留下第二个维度。

代码如下:

class Solution {
public:int maxProfit(int k, vector<int>& prices) {int n = prices.size();if(n == 0)  return 0;vector<int> buy(k+1);vector<int> sell(k+1);// k值的预处理k = min(k, n/2);// 预处理buy[0] = -prices[0];sell[0] = 0;for(int i=1; i<=k; ++i){// 不能设置为 INT_MINbuy[i] = sell[i] = INT_MIN / 2;}for(int i=1; i<n; ++i){buy[0] = max(buy[0], sell[0] - prices[i]);for(int j=1; j<=k; ++j){buy[j] = max(buy[j], sell[j] - prices[i]);sell[j] = max(sell[j], buy[j-1] + prices[i]);}}return *max_element(sell.begin(), sell.end());}
};

参考资料:188. 买卖股票的最佳时机 IV

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

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

相关文章

【数据库】数据库的基础知识

目录 前言 1、 查看数据库 1.1、查看所有数据库&#xff08;show databases;&#xff09; 1.2、创建数据库之后&#xff0c;查看创建的数据库的基本信息。 2、 创建数据库 2.1、直接创建数据库&#xff08;create database [数据库名];&#xff09; 2.2、创建数据库的时…

Pytest接口自动化测试实战演练

结合单元测试框架pytest数据驱动模型allure 目录 api&#xff1a; 存储测试接口conftest.py :设置前置操作目前前置操作&#xff1a;1、获取token并传入headers&#xff0c;2、获取命令行参数给到环境变量,指定运行环境commmon&#xff1a;存储封装的公共方法connect_mysql.p…

解决方案:Zotero实现参考文献中英文混排,将英文文献中的“等”转成“et al.”

Zotero 是一款非常实用且易于使用的参考文献管理工具&#xff0c;可帮助用户收集、整理和引用各种类型的文献&#xff0c;包括图书、期刊文章、网页等。在学术写作中起着重要作用。 但是其在中文世界中&#xff0c;运行起来偶尔会出现问题&#xff0c;这里记录一个问题及其解决…

隋唐洛阳“西宫”:上阳宫的GIS视角

隋唐洛阳城简介 营建 隋大业元年&#xff08;605年&#xff09;&#xff0c;在隋炀帝的授意下&#xff0c;隋代著名城市设计师宇文恺&#xff0c;在汉魏故城以西重新选址&#xff0c;历时8个月&#xff0c;日役劳工200万&#xff0c;兴建新都洛阳城。 城和苑 隋唐洛阳城采用…

eBPF技术介绍

前言 eBPF起源于linux内核&#xff0c;它可以以砂箱程序运行在操作系统内核的特权上下文&#xff0c;高效&#xff0c;安全&#xff0c;易于扩展而不需要修改内核源码或者加载内核模块。 操作系统一直是实现观测&#xff0c;安全和网络功能的最理想的地方&#xff0c;因为内核的…

优思学院|精益管理的理念是什么?

作为一个企业&#xff0c;我们都希望拥有高效率和优异的竞争力。但是&#xff0c;如何才能在竞争激烈的市场中脱颖而出&#xff1f;这时&#xff0c;精益管理理念的出现可以帮助我们。 精益管理的基本概念是什么&#xff1f; 精益管理的核心理念是通过消除浪费来实现生产效率…

Java线程间通信方式(3)

前文了解了线程通信方式中的CountDownLatch&#xff0c; Condition&#xff0c;ReentrantLock以及CyclicBarrier&#xff0c;接下来我们继续了解其他的线程间通信方式。 Phaser Phaser是JDK1.7中引入的一种功能上和CycliBarrier和CountDownLatch相似的同步工具&#xff0c;相…

辛弃疾最经典的10首词

他&#xff0c;文能挥笔填词&#xff0c;武能上马杀敌&#xff1b; 他&#xff0c;被称为“词中之龙”&#xff0c; 他&#xff0c;一生赤子&#xff0c;追求收复山河&#xff1b; 他&#xff0c;是与苏轼齐名的豪放派词人&#xff1b; 他是辛弃疾。 辛弃疾一生怀着赤子之…

IO多路复用——select函数

1.select函数原型和fd_set结构体说明 1.1 select函数原型 ​ 使用 select 这种 IO 多路转接方式需要调用一个同名函数 select&#xff0c;这个函数是跨平台的&#xff0c;Linux、Mac、Windows 都是支持的。程序员通过调用这个函数可以委托内核帮助我们检测若干个文件描述符的…

【MCS-51】51单片机结构原理

至今为止&#xff0c;MCS-51系列单片机有许多种型号的产品&#xff1a;其中又分为普通型51&#xff08;8031、8051、89S51&#xff09;和增强型52&#xff08;8032、8052、89S52等&#xff09;。它们最大的区别在于存储器配置各有差异。下面我举例子的都是8051这一系列的单片机…

STM32-HAL-定时器(无源蜂鸣器的驱动)

文章目录 一、蜂鸣器的介绍二、常用的无源蜂鸣器的电路三、测试准备四、初始化片上外设4.1 初始化定时器4的通道2为PWM输出模式4.2 编写驱动代码4.3 Logic分析仪查看波形4.4 代码分析 一、蜂鸣器的介绍 有源蜂鸣器&#xff1a; 有源蜂鸣器内部有一个发声电路,也就是“源”&…

数据湖Iceberg-Hive集成Iceberg(3)

文章目录 Hive集成Iceberg环境准备Hive与Iceberg的版本对应关系如下上传jar包&#xff0c;拷贝到Hive的auxlib目录中修改hive-site.xml&#xff0c;添加配置项启动 HMS 服务启动 Hadoop 创建和管理 Catalog默认使用 HiveCatalog指定 Catalog 类型使用 HiveCatalog使用 HadoopCa…

C++学习记录——이십 map和set

文章目录 1、setmultiset 2、map3、map::operator[] 1、set vector/list/deque等是序列式容器&#xff0c;map&#xff0c;set是关联式容器。序列式容器的特点就是数据线性存放&#xff0c;而关联式容器的数据并不是线性&#xff0c;数据之间有很强的关系。 它们的底层是平衡…

在当前互联网行情下,Android想转音视频开发,会有前景吗?

前言 近年来&#xff0c;由于三年疫情的影响&#xff0c;很多公司都开始陆陆续续的在裁员&#xff0c;Android开发工作岗位也是&#xff0c;可能有些从事Android开发的朋友还没有意识到&#xff0c;Android开发岗位正在变少&#xff0c;求职者&#xff0c;僧多粥少&#xff0c…

视频大文件传输的演变:从“卷轴男孩”到自动化

200年前&#xff0c;从纽约市到英国伦敦的单程旅行需要乘坐一艘跨大西洋轮船将近三周——如果你能负担得起的话&#xff0c;那就是。那些不能在满是汗水、狭窄的帆船上安顿大约一个半月的人。 今天&#xff0c;视频专业人士能够在几小时甚至几分钟内跨越相同的物理距离传输大量…

《用于估计血压变化的光电体积描记图和心电图的特征》阅读笔记

目录 一、摘要 二、十大问题 Q1论文试图解决什么问题&#xff1f; Q2这是否是一个新的问题&#xff1f; Q3这篇文章要验证一个什么科学假设&#xff1f; Q4有哪些相关研究&#xff1f;如何归类&#xff1f;谁是这一课题在领域内值得关注的研究员&#xff1f; Q5论文中提…

微信小程序第五节——登录那些事儿(超详细的前后端完整流程)

&#x1f4cc; 微信小程序第一节 ——自定义顶部、底部导航栏以及获取胶囊体位置信息。 &#x1f4cc; 微信小程序第二节 —— 自定义组件 &#x1f4cc; 微信小程序第三节 —— 页面跳转的那些事儿 &#x1f4cc; 微信小程序第四节—— 网络请求那些事儿 &#x1f61c;作 …

MFC之CRect详解

2023年4月25日&#xff0c;周二晚上。 今天查了不少关于CRect类及其相关内容的资料&#xff0c;学到了不少东西&#xff0c;所以我决定写一篇详细的关于CRect类及其相关内容的文章&#xff0c;以记录今天所学。 CRect类 在 MFC 中&#xff0c;CRect 类表示一个矩形区域。它是…

linux 命令之 tar -czvf和 tar -xzvf

文章目录 一、概述&#xff1a;二、基础知识 一、概述&#xff1a; tar 用于linux 系统中压缩和解压 二、基础知识 tar常用命令参数说明 tar命令的czvf/xzvf参数分别代表的意义如下&#xff1a; -c 或–create 建立新的备份文件。 -x或–extract或–get 从备份文件中还原文件…

SparkStreaming学习之——无状态与有状态转化、遍历kafka的topic消息、WindowOperations

目录 一、状态转化 二、kafka topic A→SparkStreaming→kafka topic B (一)rdd.foreach与rdd.foreachPartition (二)案例实操1 1.需求&#xff1a; 2.代码实现&#xff1a; 3.运行结果 (三)案例实操2 1.需求&#xff1a; 2.代码实现&#xff1a; 3.运行结果 三、W…