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

代码随想录算法训练营第四十七天丨 动态规划part10

121. 买卖股票的最佳时机

思路

动态规划

动规五部曲分析如下:

  • 确定dp数组(dp table)以及下标的含义

dp[i][0] 表示第i天持有股票所得最多现金 ,这里可能有疑惑,本题中只能买卖一次,持有股票之后哪还有现金呢?

其实一开始现金是0,那么加入第i天买入股票现金就是 -prices[i], 这是一个负数。

dp[i][1] 表示第i天不持有股票所得最多现金

注意这里说的是“持有”,“持有”不代表就是当天“买入”!也有可能是昨天就买入了,今天保持持有的状态

很多人把“持有”和“买入”没区分清楚。

在下面递推公式分析中,我会进一步讲解。

  • 确定递推公式

如果第i天持有股票即dp[i][0], 那么可以由两个状态推出来

  • 第i-1天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金 即:dp[i - 1][0]
  • 第i天买入股票,所得现金就是买入今天的股票后所得现金即:-prices[i]

那么dp[i][0]应该选所得现金最大的,所以dp[i][0] = max(dp[i - 1][0], -prices[i]);

如果第i天不持有股票即dp[i][1], 也可以由两个状态推出来

  • 第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金 即:dp[i - 1][1]
  • 第i天卖出股票,所得现金就是按照今天股票价格卖出后所得现金即:prices[i] + dp[i - 1][0]

同样dp[i][1]取最大的,dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);

这样递推公式我们就分析完了

  • dp数组如何初始化

由递推公式 dp[i][0] = max(dp[i - 1][0], -prices[i]); 和 dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);可以看出

其基础都是要从dp[0][0]和dp[0][1]推导出来。

那么dp[0][0]表示第0天持有股票,此时的持有股票就一定是买入股票了,因为不可能有前一天推出来,所以dp[0][0] -= prices[0];

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

  • 确定遍历顺序

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

  • 举例推导dp数组

以示例1,输入:[7,1,5,3,6,4]为例,dp数组状态如下:

121.买卖股票的最佳时机

dp[5][1]就是最终结果。

为什么不是dp[5][0]呢?

因为本题中不持有股票状态所得金钱一定比持有股票状态得到的多!

以上分析完毕,代码如下:

class Solution {public int maxProfit(int[] prices) {//题目意思:一只给定股票第i天的价格为prices[i],求所能获得的最大利润//确定bp数组及其下标含义//dp[i][0] 表示持有这只股票时身上现金多少  dp[i][1]表示不持有这只股票时身上的现金多少int[][] dp = new int[prices.length+1][2];//确定递推公式//初始化dp数组dp[0][0] = -prices[0];dp[0][1] = 0;for (int i = 1; i < prices.length; i++) {//dp[i][0] 是指 上一天的持有这只股票时的现金多少 或 今天买入这只股票时持有的现金多少 的最大值dp[i][0] = Math.max(dp[i-1][0],-prices[i]);//dp[i][1] 是指 上一天持有这只股票,但今天把这只股票卖了时的现金多少 或 上一天不持有这只股票时身上持有的现金 的最大值dp[i][1] = Math.max(dp[i-1][1],dp[i][0]+prices[i]);}return Math.max(dp[prices.length-1][0],dp[prices.length-1][1]);}
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

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

思路

本题我们在讲解贪心专题的时候就已经讲解过了贪心算法:买卖股票的最佳时机II (opens new window),只不过没有深入讲解动态规划的解法,那么这次我们再好好分析一下动规的解法。

本题和121. 买卖股票的最佳时机 (opens new window)的唯一区别是本题股票可以买卖多次了(注意只有一只股票,所以再次购买前要出售掉之前的股票)

在动规五部曲中,这个区别主要是体现在递推公式上,其他都和121. 买卖股票的最佳时机 (opens new window)一样一样的

所以我们重点讲一讲递推公式。

这里重申一下dp数组的含义:

  • dp[i][0] 表示第i天持有股票所得现金。
  • dp[i][1] 表示第i天不持有股票所得最多现金

如果第i天持有股票即dp[i][0], 那么可以由两个状态推出来

  • 第i-1天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金 即:dp[i - 1][0]
  • 第i天买入股票,所得现金就是昨天不持有股票的所得现金减去 今天的股票价格 即:dp[i - 1][1] - prices[i]

注意这里和121. 买卖股票的最佳时机 (opens new window)唯一不同的地方,就是推导dp[i][0]的时候,第i天买入股票的情况

在121. 买卖股票的最佳时机 (opens new window)中,因为股票全程只能买卖一次,所以如果买入股票,那么第i天持有股票即dp[i][0]一定就是 -prices[i]。

而本题,因为一只股票可以买卖多次,所以当第i天买入股票的时候,所持有的现金可能有之前买卖过的利润。

那么第i天持有股票即dp[i][0],如果是第i天买入股票,所得现金就是昨天不持有股票的所得现金 减去 今天的股票价格 即:dp[i - 1][1] - prices[i]。

再来看看如果第i天不持有股票即dp[i][1]的情况, 依然可以由两个状态推出来

  • 第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金 即:dp[i - 1][1]
  • 第i天卖出股票,所得现金就是按照今天股票价格卖出后所得现金即:prices[i] + dp[i - 1][0]

注意这里和121. 买卖股票的最佳时机 (opens new window)就是一样的逻辑,卖出股票收获利润(可能是负值)天经地义!

代码如下:(注意代码中的注释,标记了和121.买卖股票的最佳时机唯一不同的地方)

class Solution {public int maxProfit(int[] prices) {//题目意思:一只给定股票第i天的价格为prices[i],求所能获得的最大利润//确定bp数组及其下标含义//dp[i][0] 表示持有这只股票时身上现金多少  dp[i][1]表示不持有这只股票时身上的现金多少int[][] dp = new int[prices.length+1][2];//确定递推公式//初始化dp数组dp[0][0] = -prices[0];dp[0][1] = 0;for (int i = 1; i < prices.length; i++) {//dp[i][0] 是指 上一天的持有这只股票时的现金多少 或 今天买入这只股票时持有的现金多少 的最大值dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]-prices[i]);//dp[i][1] 是指 上一天持有这只股票,但今天把这只股票卖了时的现金多少 或 上一天不持有这只股票时身上持有的现金 的最大值dp[i][1] = Math.max(dp[i-1][1],dp[i][0]+prices[i]);}return Math.max(dp[prices.length-1][0],dp[prices.length-1][1]);}
}

http://www.lryc.cn/news/224718.html

相关文章:

  • 微前端:quankun
  • CSDN每日一题学习训练——Java版(克隆图、最接近的三数之和、求公式的值)
  • XOR Construction
  • K8S容器持续Terminating无法正常关闭(sider-car容器异常,微服务容器正常)
  • Spring 循环依赖
  • MySQL 8.0.13升级到8.0.35记录 .NET
  • flink udtaf 常年不能用
  • 路由汇总的四要点
  • HashMap存值、取值及哈希碰撞原理分析
  • 【SVN】
  • 编程语言,脚本语言
  • 探索双十一:从技术角度剖析电商狂欢节
  • Ubuntu LTS 坚持 10 年更新不动摇
  • Python将多个相同格式的变量存储到列表中
  • 前端字符串转数组对象实现方式-开发bug总结6
  • 99 颜色分类
  • 计算机视觉与深度学习 | 基于GPS/BDS多星座加权图因子优化的行人智能手机导航
  • 低代码平台,业务开发的“银弹”
  • 补偿 FIR 滤波器引入的延迟
  • 图数据库Neo4j详解
  • 系列一、Shiro概述
  • SpringCloudAlibaba——Sentinel
  • Java编写简易rabbitmq生产者与消费者
  • 3.0.3版vsftpd所支持的FTP命令
  • OTA包添加自定义内容
  • Luatos Air700 改变BL0942串口波特率
  • 不可忽视的国外服务器地址IP选择指南
  • C语言 预处理详解
  • c++ async 使用详解,创建异步任务的多种方法
  • 万物皆数——用matlab求解二阶微分方程