LeetCode 188.买卖股票的最佳时机Ⅳ:
思路:
这道题思路与买卖股票的最佳时机Ⅲ相似,那道题的限定股票最多买卖次数为2,这道题是限定股票最多买卖次数为k,需要将原来4个状态扩展为k个持有和k个不持有的状态
动规五部曲:
- dp数组及含义:
dp[i][j]:第 i 天状态 j 时所拥有的收益。
和之前题目思路一样,
dp[i][0]是第一次持有(也可以dp[i][0]是不操作,那么递推公式也有变化)
dp[i][1]是第一次不持有
dp[i][2]是第二次持有
dp[i][3]是第二次不持有
…
dp[i][2k - 2]是第k次持有
dp[i][2k - 1]是第k次不持有
j 的范围为[0, 2k - 1],数组的列数为 2 k - 递推公式:
dp[i][0] = max(继承之前持有的,之前没持有今天买入)
= max(dp[i - 1][0], -prices[i])
dp[i][[1] = max(继承昨天没持有的,之前持有今天卖出)=max(dp[i - 1][1], dp[i - 1][0] + prices[i])
当j ∈ {2, 4, 6, … 2*k - 2}时
dp[i][j] = max(继承之前持有的,之前没持有今天买入)=max(dp[i - 1][j], dp[i - 1][j - 1] - prices[i])
dp[i][j + 1] = max(继承昨天没持有的,之前持有今天卖出)=max(dp[i - 1][j + 1], dp[i - 1][j] + prices[i])
(PS:如果dp数组定义时,dp[i][0]是不操作,那么第一次持有和不持有的公式与后面的相同) - 初始化:
由递推公式可知,要初始化第0行,而第0行就是第1天,如果持有股票那么一定是今天买入了,收益为-prices[0];没持有股票可能今天买入又卖出了,收益为0;(后面第 i 天持有股票不一定是第 i 天买入了)
又观察dp数组的列规律得,j % 2 = 0的列都是持有,j % 2 = 1的列都是不持有。因此将持有的列初始化为-prices[0] - 遍历顺序:
从前往后 - 举例
k = 2为例
代码
class Solution:
def maxProfit(self, k: int, prices: List[int]) -> int:
if len(prices) <= 1:
return 0
len_prices = len(prices)
# 初始化
dp = [[0] * (2*k) for _ in range(len_prices)]
for j in range(0, 2*k, 2):
dp[0][j] = -prices[0]
# 遍历
for i in range(1, len_prices):
# dp[i][0]和dp[i][1]单独修改
dp[i][0] = max(dp[i - 1][0], -prices[i]) # 第一次持有股票
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i]) # 第一次不持有股票
for j in range(2, 2*k, 2):
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1] - prices[i]) # 第j/2 + 1次持有
dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] + prices[i]) # 第j/2+1次不持有
return dp[len_prices - 1][2*k - 1]
LeetCode 309.最佳买卖股票时机含冷冻期:
思路:
还是需要分析题目中有几个状态,题目中说有冷冻期,在冷冻期时无法买入股票。
- 首先
第 i 天的状态可以分为持有股票和不持有股票 - 其次
而因为有冷冻期的原因,不持有股票的状态可以分为如下:- 第 i 天当天卖出股票
- 第 i 天为冷冻期
- 第 i 天为冷冻期之后一直没有买入股票(买入股票的话会变成持有股票的状态,而买入又卖出的话就进入了新一轮的冷静期)
动规五部曲:
- dp数组及含义:
dp[i][j] 为第 i 天状态 j 所拥有的收益- dp[i][0]:第 i 天持有股票
- dp[i][1]:第 i 天当天卖出股票
- dp[i][2]:第 i 天为冷冻期
- dp[i][3]第 i 天为冷冻期之后一直没有买入股票
- 递推公式:
① 状态0,要么昨天就持有股票dp[i - 1][0];要么昨天不持有股票,今天买入股票,要求昨天没有卖出股票(即状态1)dp[i - 1][2] - prices[i],dp[i - 1][3] - prices[i]。max(dp[i - 1][0],dp[i - 1][2] - prices[i],dp[i - 1][3] - prices[i])
② 状态1:那么昨天一定持有股票,然后今天卖出,dp[i - 1][0] + prices[i]
③ 状态2:昨天一定卖出股票,冷冻期不能操作股票dp[i - 1][1]
④ 状态3:昨天要么是冷冻期(状态2),要么是冷冻期之后没有买入股票(状态3)。max(dp[i - 1][2], dp[i - 1][3]) - 初始化:
由递推公式可知,应当初始化dp[0]这一行,dp[0][0]初始化为 - prices[0],其余状态感觉不好初始化,如果初始化为某个数,那么第 1 天买入了股票的收益就变成了 x - prices[1],x只有为0才合理,因此其余状态初始化为0 - 遍历方式
从前往后 - 举例:
class Solution:
def maxProfit(self, prices: List[int]) -> int:
if len(prices) <= 1:
return 0
len_prices = len(prices)
dp = [[0] * 4 for _ in range(len_prices)]
# 初始化
dp[0][0] = -prices[0]
# 遍历
for i in range(1, len_prices):
dp[i][0] = max(dp[i - 1][0], dp[i - 1][2] - prices[i], dp[i - 1][3] - prices[i]) # 持有
dp[i][1] = dp[i - 1][0] + prices[i] # 今天卖出
dp[i][2] = dp[i - 1][1] # 冷冻期
dp[i][3] = max(dp[i - 1][2], dp[i - 1][3]) # 冷冻期之后一直没买入
# 在不持有的状态中取最大值
return max(dp[len_prices - 1][1], dp[len_prices - 1][2], dp[len_prices - 1][3])
LeetCode 714.买卖股票的最佳时机含手续费:
思路:
相对于买卖股票的最佳时机Ⅱ多了个手续费。
分析题目可知,本题能够进行多轮买卖,买卖时收取手续费,那么相对于买卖股票的最佳时机Ⅱ,多了个手续费fee,区别只在于递推公式。
采取卖股票时才收取手续费。
递推公式:
- 持有股票,要么昨天就持有;要么昨天不持有,今天买入
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] - fee)
class Solution:
def maxProfit(self, prices: List[int], fee: int) -> int:
if len(prices) <= 1:
return 0
len_prices = len(prices)
# 初始化
dp = [[0] * 2 for _ in range(len_prices)]
dp[0][0] = -prices[0]
# 遍历
for i in range(1, len_prices):
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]) # 持有
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee) # 不持有,记得-fee
return dp[len_prices - 1][1]
学习收获:
学习了限定股票交易次数为k、含冷冻期和收手续费的情况。其中交易次数为 k 和收手续费都是在之前股票交易的题目上的改进;含冷冻期需要对不持有的状态做进一步分类:冷冻期前(当天卖出)、冷冻期和冷冻期后一直没买入