Day51【动态规划】309.最佳买卖股票时机含冷冻期、714.买卖股票的最佳时机含手续费

news/2024/11/7 22:46:34/

309.最佳买卖股票时机含冷冻期

力扣题目链接/文章讲解 

视频讲解 

记住昨天的回顾总结提到的:应该灵活利用 dp 数组的下标描述所有状态  

动态规划五部曲

1、确定 dp 数组下标及值含义

dp[i][j],第 i 天状态为 j,所剩的最多现金为 dp[i][j]

这样,通过两个下标,能够描述在某天及当天的股票持有状态

每天具体可以区分出如下四个状态:

  • 状态一:持有股票状态(今天买入股票,或者是之前就买入了股票然后没有操作,一直持有)
  • 非冷冻期且不持有股票状态,需区分两种卖出股票状态(区分的原因:为了推冷冻期状态)
    • 状态二:保持卖出股票的状态(两天前就卖出了股票,度过一天冷冻期。或者是前一天就是卖出股票状态,一直没操作)
    • 状态三:今天卖出股票
  • 状态四:今天为冷冻期状态,但冷冻期状态不可持续,只有一天!

dp[i][0]:下标表示在第 i 天,持有股票的状态,值为当前状态下的最大利润

dp[i][1]:下标表示在第 i 天,保持卖出股票的状态,值为当前状态下的最大利润

dp[i][2]:下标表示在第 i 天,当天卖出股票的状态,值为当前状态下的最大利润

dp[i][3]:下标表示在第 i 天,当前为冷冻期的状态,值为当前状态下的最大利润

2、确定递推公式 

需要分别思考 dp[i][0]、dp[i][1]、dp[i][2]、dp[i][3] 应该怎么推

dp[i][0]:表示在第 i 天,持有股票的情况下的最大利润,我们需要考虑怎么从前一天转移到当前状态

  • 可能在第 i - 1 天就已经持有股票了,即 dp[i - 1][0],到了第 i 天只需要保持状态不操作
  • 可能在第 i - 1 天非冷冻期且不持有股票,那么我们可以在第 i 天买入,即 dp[i - 1][1] - prices[i]
  • 可能在第 i - 1 天为冷冻期,我们也可以在第 i 天买入,即 dp[i - 1][3] - prices[i]

因为需要最大利润,所以 dp[i][0] = max(dp[i - 1][0], dp[i - 1][3] - prices[i], dp[i - 1][1] - prices[i]) 

dp[i][1]:表示在第 i 天,保持卖出股票的情况下的最大利润

  • 可能在第 i - 1 天就已经保持卖出股票的状态了,即 dp[i - 1][1]
  • 可能在第 i - 1 天为冷冻期,那么到了第 i 天就为非冷冻期且卖出股票的状态,即 dp[i - 1][3]

因为需要最大利润,所以 dp[i][1] = max(dp[i - 1][1], dp[i - 1][3])

dp[i][2]:表示在第 i 天,当天卖出股票的情况下的最大利润

这种只可能是在 i - 1 天持有股票的情况下,再在第 i 天卖出,即 dp[i][2] = dp[i - 1][0] + prices[i]

dp[i][3]:表示在第 i 天,当天为冷冻期的情况下的最大利润

这种只可能是在第 i - 1 天当天卖出的情况下,到了第 i 天才为冷冻期,即 dp[i][3] = dp[i - 1][2] 

3、初始化 dp 数组

根据递推公式,第 i 天的 dp 值都是从第 i - 1 天的 dp 值推导出来的,我们需要初始化dp[0][j]

如果是持有股票状态(状态一)那么:dp[0][0] = -prices[0],一定是当天买入股票

保持卖出股票状态(状态二),这里其实从 「状态二」的定义来说 ,很难明确应该初始多少,这种情况我们就看递推公式需要我们给他初始成什么数值

如果 i 为 1,第 1 天买入股票,那么递归公式中需要计算 dp[i - 1][1] - prices[i] ,即 dp[0][1] - prices[1],那么大家感受一下 dp[0][1] (即第0天的状态二)应该初始成多少,只能初始为0。如果初始为其他数值,是我们第1天买入股票后手里还剩的现金数量就不对了

今天卖出了股票(状态三),同上分析,dp[0][2]初始化为0,dp[0][3]也初始为0

对于通过定义难以确定如何初始化的情况,应该结合递推公式确定如何初始化 

4、确定遍历顺序

从递推公式可以看出 dp[i] 都是由 dp[i - 1] 推导出来的,那么一定是从前向后遍历 

5、打印 dp 数组验证

代码如下

class Solution {
public:int maxProfit(vector<int>& prices) {int n = prices.size();if (n == 0) return 0;vector<vector<int>> dp(n, vector<int>(4, 0));dp[0][0] -= prices[0]; // 持股票for (int i = 1; i < n; i++) {   // i为0时已经被初始化过了,从i为1开始遍历填充dp数组// 四种情况dp[i][0] = max(max(dp[i - 1][0], dp[i - 1][3] - prices[i]), dp[i - 1][1] - prices[i]);dp[i][1] = 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 max(dp[n - 1][3], max(dp[n - 1][1], dp[n - 1][2]));}
};

注意,最后的结果一定是到达最后一天,且不持有股票状态或冷冻期状态

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

力扣题目链接/文章讲解 

视频讲解 

本题假设在卖出的时候支付手续费 

动态规划五部曲!

1、确定 dp 数组下标及值的含义 

先想想本题 dp 应该怎么定义,别忘了之前说的,dp 数组的下标能够表示状态

在股票问题中,某个状态需要描述在某天,及是否持有股票

因此我们定义 dp 数组下标及值含义:

dp[i][0]:下标表示在第 i 天,未持有股票,值表示第 i 天未持有股票所得最多现金

dp[i][1]:下标表示在第 i 天,持有股票,值表示第 i 天持有股票所得最多现金

通过第一个下标 i 描述在哪一天,第二个下标为 0 或 1 描述在该天未持有或持有股票,这样通过两个下标就能够描述所有的状态了

2、确定递推公式

分别思考 dp[i][0] 和 dp[i][1] 分别应该怎么推

dp[i][0]:需要求第 i 天未持有股票所得最多现金,要考虑怎么转移到第 i 天未持有股票的这种状态

可能是在第 i 天卖出了股票,所得现金就是昨天持有股票,然后今天卖出股票后所得现金,注意考虑卖出需要支付手续费,即:dp[i - 1][1](昨天持有股票)+ prices[i](今天卖出股票)- fee
可能是在第 i - 1 天就未持有股票,那么就保持现状,所得现金就是昨天未持有股票的所得现金,即:dp[i - 1][0](昨天未持有股票)
因为求的最多现金,即 dp[i][0] = max(dp[i - 1][1] + prices[i] - fee[i], dp[i - 1][0]) 

同理,dp[i][1]:

可能是在第 i 天买入股票,所得现金为 dp[i - 1][0](昨天未持有股票)- prices[i](今天买入股票)(因为一只股票可以买卖多次,所以当第 i 天买入股票的时候,所持有的现金需要考虑之前买卖过的利润)
可能是在第 i - 1 天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金,即:dp[i - 1][1](昨天持有股票)
因为求的最多现金,即 dp[i][1] = max(dp[i - 1][0] - prices[i], dp[i - 1][1])  

3、dp 数组初始化

从递推公式看出,求某一天 dp 需要知道其前一天的 dp 值,即都是要从dp[0][0]和dp[0][1]推导出来

那么dp[0][0]表示第 0 天未持有股票,不持有股票现金就是 0,所以 dp[0][0] = 0

dp[0][1]表示第 0 天持有股票,持有股票一定是在第 0 天买入了股票,所以 dp[0][1] = -prices[0]

4、确定遍历顺序

从递推公式可以看出 dp[i] 都是由 dp[i - 1] 推导出来的,那么一定是从前向后遍历

5、打印 dp 数组验证

代码如下

class Solution {
public:int maxProfit(vector<int>& prices, int fee) {vector<vector<int> > dp(prices.size(), vector<int>(2));dp[0][0] = 0;   // 第0天不持股dp[0][1] = -prices[0];  // 第0天买入股票for (int i = 1; i < prices.size(); ++i) {dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i] - fee);dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]);}return max(dp[prices.size() - 1][0], dp[prices.size() - 1][1]);}
};

滚动数组优化的代码略 

本题和 122.买卖股票的最佳时机II 的区别就是这里需要多一个减去手续费的操作 


回顾总结 

应该灵活利用 dp 数组的下标描述所有状态

对于定义难以确定如何初始化的情况,应该结合递推公式确定如何初始化 

 


http://www.ppmy.cn/news/83289.html

相关文章

Mybatis连接MySQL数据库通过逆向工程简化开发流程

文章目录 一、使用步骤1、建立新项目2、引入pom依赖3、创建逆向工程的配置文件 generatorConfig.xml4、运行逆行工程&#xff0c;生成代码文件 二、案例展示1、建立数据表2、改写对应的配置文件内容1、数据库连接配置,指定自己的数据库2、配置pojo生成的位置3、配置sql映射文件…

使用 Docker 安装常用的服务

Docker 常见服务的安装 Docker 安装 MySQL【详细介绍&#xff0c;后续只会简单安装】 1.查看可用的 MySQL 版本 docker search mysql拉取 MySQL 镜像 docker pull mysql:latest查看本地镜像 docker images运行容器 注意使用 -p 来指定外面和容器里面的端口&#xff1b; -…

【多线程】线程的可见性

目录 一、什么是线程的可见性二、可见性问题示例2.1 代码2.2 截图 三、解决可见性问题3.1 volatile关键字3.2 synchronized关键字 四、用volatile关键字解决可见性问题示例4.1 代码4.2 截图 五、用synchronized关键字解决可见性问题示例5.1 代码5.2 截图 六、可见性与原子性 一…

放大镜-第14届蓝桥杯省赛Scratch中级组真题第3题

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第138讲。 放大镜&#xff0c;本题是2023年5月7日举行的第14届蓝桥杯省赛Scratch图形化编程中级组编程第3题&#xff0…

Linux系统安装Redis

1.远程下载稳定版本 wget http://download.redis.io/releases/redis-6.0.7.tar.gz 编译 redis-6.x&#xff0c;要求 C11 编译器&#xff08;因为redis是用c写的&#xff09;&#xff0c;否则会遇到大量如下所示的错误&#xff1a; server.h:1051:5: 错误&#xff1a;expected…

音视频八股文(6)-- ffmpeg大体介绍和内存模型

播放器框架 常用音视频术语 • 容器&#xff0f;文件&#xff08;Conainer/File&#xff09;&#xff1a;即特定格式的多媒体文件&#xff0c; 比如mp4、flv、mkv等。 • 媒体流&#xff08;Stream&#xff09;&#xff1a;表示时间轴上的一段连续数据&#xff0c;如一 段声音…

整合Springboot+MybatisPlus+达梦数据库

1、安装Windows环境的达梦数据库可视化软件 这里不做安装介绍 安装步骤很简单&#xff0c;提供的软件也很全面&#xff0c;特别是数据库迁移工具&#xff0c;支持市面上许多主流的大型数据库&#xff0c;例如&#xff1a;Oracle、SQLServer、MySQL、DB2、PostgreSQL、Informix…

centos下编译ffmpeg+ libfdk_aac +x264

因为FFmpeg自带的AAC编码器已经废弃了AV_SAMPLE_FMT_S16格式PCM编码AAC,如果使用FFmpeg自带的AAC编码器,就需要做音频的重采样(AV_SAMPLE_FMT_S16重采样为:AV_SAMPLE_FMT_FLTP), 如果不想自己做重采样转换,可以使用libfdk-aac这库。 编译FFMPEG之前,先编译好libfdk-aac…