【动态规划】股票买卖问题
股票买卖问题
简介
在力扣上,股票买卖问题都可以通过动态规划求解,具体的题目如下:
序号 | 题目 | 区别 |
---|---|---|
1 | 121. 买卖股票的最佳时机 | 只能交易一次 |
2 | 122. 买卖股票的最佳时机 II | 交易次数不受限制 |
3 | 123. 买卖股票的最佳时机 III | 交易次数受限制 |
4 | 188. 买卖股票的最佳时机 IV | 交易次数为K(可能受限,也可能无限制) |
5 | 309. 最佳买卖股票时机含冷冻期 | 交易次数不受限制,但包含冷冻期 |
6 | 714. 买卖股票的最佳时机含手续费 | 交易次数不受限制,但包含手续费 |
一种常用的方法是将「买入」和「卖出」分开进行考虑:「买入」为负收益,而「卖出」为正收益。在初入股市时,你只有「买入」的权利,只能获得负收益。而当你「买入」之后,你就有了「卖出」的权利,可以获得正收益。
显然,我们需要尽可能地降低负收益而提高正收益,因此我们的目标总是将收益值最大化。因此,我们可以使用动态规划的方法,维护在股市中每一天结束后可以获得的「累计最大收益」,并以此进行状态转移,得到最终的答案。
应用
应用1:Leetcode.121
题目
分析
设
边界条件
未开始交易的时候,收益为零,即
状态转移
显然,要获取最大的收益,我们首先想到的是在最低点买入,然后在最高点卖出,这样,我们就可以获取最大收益。
我们假设前
-
保留股票,收益与前一天的收益相同,即
; -
卖出股票,收益就是当天的价格与历史最低价格之差,即
;
所以,在第
代码实现
class Solution { public int maxProfit(int[] prices) { int n = prices.length; int minPrice = prices[0]; int[] dp = new int[n]; int result = dp[0]; for (int i = 1; i < n; i++) { minPrice = Math.min(minPrice, prices[i]); dp[i] = Math.max(dp[i - 1], prices[i] - minPrice); result = Math.max(dp[i], result); } return result; } }
应用2:Leetcode.122
题目
分析
设
边界条件
显然,第零天不持有股票,最大收益为零,第零天持有股票,但是由于没有卖出,所以,最大收益为负无穷,即:
状态转移
那么,在第
-
手上持有股票
-
在第
天,就已经持有有股票,第 天没有买入,收益与前一天的收益相同,即 ; -
在第
天,未持有股票,在第 天买入股票,收益为第 天的收益与第 天买入的成本之差,即 。
因此,在第
天结束后,若手上持有股票,那么最大收益,就是上述两种情况的最大值,所以,状态转移方程为: -
-
手上不持有股票
-
在第
天手上就已经持有股票,在第 天结束卖出,那么收益为前一天的收益与当天卖出的股票价格之和,即 ; -
在第
天结束就已经卖出,第 没有进行交易,那么收益,与前一天的收益相同,即 。
因此,在第
天结束后,若手上不持有股票,那么最大收益,就是上述两种情况的最大值,所以,状态转移方程为: -
所以,综上,状态转移方程为:
注意,第
代码实现
class Solution { public int maxProfit(int[] prices) { int n = prices.length; int[][] dp = new int[n + 1][2]; dp[0][1] = Integer.MIN_VALUE; dp[0][0] = 0; for (int i = 1; i <= n; i++) { dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i - 1]); dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i - 1]); } return dp[n][0]; } }
应用3:Leetcode.123
题目
分析
设
边界条件
显然,不管最大交易次数为多少次,第零天不持有股票,最大收益为零,第零天持有股票,但是由于没有卖出,所以,最大收益为负无穷,即:
状态转移
那么,假设最大交易次数限制为
-
手上持有股票
-
在第
天,就已经持有有股票,第 天没有买入,收益与前一天的收益相同,即 ; -
在第
天,未持有股票,在第 天买入股票,收益为第 天的收益与第 天买入的成本之差,即 。
因此,在第
天结束后,若手上持有股票,那么最大收益,就是上述两种情况的最大值,所以,状态转移方程为: -
-
手上不持有股票
-
在第
天手上就已经持有股票,在第 天结束卖出,那么收益为前一天的收益与当天卖出的股票价格之和,也就是说,在第 天结束前,就已经进行了第 次买入,最后一天将股票卖出,完成第 次交易,即 ; -
在第
天结束就已经卖出,第 没有进行交易,那么收益,与前一天的收益相同,也就是说,前一天就已经完成了 次交易,即 。
因此,在第
天结束后,若手上不持有股票,那么最大收益,就是上述两种情况的最大值,所以,状态转移方程为: -
所以,综上,状态转移方程为:
对于本题,我们令
代码实现
class Solution { public int maxProfit(int[] prices) { return maxProfit(prices, 2); } private int maxProfit(int [] prices, int k) { int n = prices.length; int [][][] dp = new int [n + 1][k + 1][2]; for (int i = 0; i <= k; i++) { dp[0][i][0] = 0; dp[0][i][1] = Integer.MIN_VALUE; } for (int i = 1; i <= n; i++) { for (int j = 1; j <= k; j++) { dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i - 1]); dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i - 1]); } } return dp[n][k][0]; } }
应用4:Leetcode.188
题目
分析
假设数组
所以,按照
代码实现
class Solution: def maxProfit(self, k: int, prices: List[int]) -> int: if k == 0: return 0 if k > len(prices) // 2: return self.maxProfit1(prices) else: return self.maxProfit2(k, prices) def maxProfit1(self, prices: List[int]) -> int: n = len(prices) dp = [[0 for _ in range(2)] for _ in range(n + 1)] dp[0][0] = 0 dp[0][1] = -float("inf") for i in range(1, n + 1): dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i - 1]) dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i - 1]) return dp[-1][0] def maxProfit2(self, K: int, prices: List[int]) -> int: n = len(prices) dp = [[[0 for _ in range(2)] for _ in range(K + 1)] for _ in range(n + 1)] for _k in range(1, K + 1): dp[0][_k][0] = 0 dp[0][_k][1] = -float("inf") for i in range(1, n + 1): for _k in range(K, 0, -1): dp[i][_k][0] = max(dp[i - 1][_k][0], dp[i - 1][_k][1] + prices[i - 1]) dp[i][_k][1] = max(dp[i - 1][_k][1], dp[i - 1][_k - 1][0] - prices[i - 1]) return dp[-1][K][0]
应用5:Leetcode.309
题目
分析
设
注意,这里的「处于冷冻期」指的是在第
边界条件
显然,第零天不持有股票,最大收益为零,第零天持有股票,但是由于没有卖出,所以,最大收益为负无穷,即:
状态转移
由于股票买卖需要间隔一天作为冷冻期,所以从卖出的状态转移时,中间要间隔一天。
那么,在第
-
手上持有股票
- 在第
天,就已经持有有股票,第 天没有买入,收益与前一天的收益相同,即 ; - 在第
天,不持有股票,在第 天买入股票,收益为第 天的收益与第 天买入的成本之差,即 。
因为,如果要第 天买入股票,那么,我们就要保证第 天手上不持有股票,且不处于冷冻期,因此,必须要保证第 天,就不持有股票。
综上,在第
天结束后,若手上持有股票,那么最大收益,就是上述两种情况的最大值,所以,状态转移方程为: - 在第
-
手上不持有股票
- 在第
天手上就已经持有股票,在第 天结束卖出,那么收益为第 天的收益与当天卖出的股票价格之和,即 ; - 在第
天结束就已经卖出,第 没有进行交易,那么收益,与第 天的收益相同,即 。
综上,在第
天结束后,若手上不持有股票,那么最大收益,就是上述两种情况的最大值,所以,状态转移方程为: - 在第
所以,综上,状态转移方程为:
代码实现
from typing import List class Solution: def maxProfit(self, prices: List[int]) -> int: n = len(prices) dp = [[0 for _ in range(2)] for i in range(n + 1)] dp[0][0] = 0 dp[0][1] = -float("inf") for i in range(1, n + 1): dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i - 1]) dp[i][1] = max(dp[i - 1][1], dp[i - 2][0] - prices[i - 1]) return dp[n][0]
应用6:Leetcode.714
题目
分析
设
边界条件
显然,第零天不持有股票,最大收益为零,第零天持有股票,但是由于没有卖出,所以,最大收益为负无穷,即:
状态转移
那么,在第
-
手上持有股票
- 在第
天,就已经持有有股票,第 天没有买入,收益与前一天的收益相同,即 ; - 在第
天,不持有股票,在第 天买入股票,收益为第 天的收益与第 天买入的成本之差,即 。
综上,在第
天结束后,若手上持有股票,那么最大收益,就是上述两种情况的最大值,所以,状态转移方程为: - 在第
-
手上不持有股票
- 在第
天,就已经持有股票,第 天结束卖出,那么收益为第 天的收益与当天卖出的股票收益之和,即 ;
注意,卖出股票的收益等于卖出时股票的价格减去手续费。 - 在第
天,不持有股票,第 没有进行交易,那么收益,与第 天的收益相同,即 。
综上,在第
天结束后,若手上不持有股票,那么最大收益,就是上述两种情况的最大值,所以,状态转移方程为: - 在第
所以,综上,状态转移方程为:
代码实现
class Solution: def maxProfit(self, prices: List[int], fee: int) -> int: n = len(prices) dp = [[0 for _ in range(2)] for i in range(n + 1)] dp[0][0] = 0 dp[0][1] = -float("inf") for i in range(1, n + 1): dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i - 1]- fee) dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i - 1]) return dp[-1][0]
参考:
本文作者:LARRY1024
本文链接:https://www.cnblogs.com/larry1024/p/17041468.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步