买卖股票的最佳时机:无限次/冷冻期/k次【基础算法精讲 21】_哔哩哔哩_bilibili
122. 买卖股票的最佳时机 II
难度中等2120收藏分享切换为英文接收动态
给你一个整数数组 prices
,其中 prices[i]
表示某支股票第 i
天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
一、递归搜索 + 保存计算结果 = 记忆化搜索
由于最后一天不能持有股票,所以递归入口的式子是等价的
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
@cache
def dfs(i: int, hold: bool) -> int:
if i < 0:
return -inf if hold else 0
if hold:
return max(dfs(i - 1, True), dfs(i - 1, False) - prices[i])
return max(dfs(i - 1, False), dfs(i - 1, True) + prices[i])
return dfs(n - 1, False)
二、1:1 翻译成递推
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
f = [[0] * 2 for _ in range(n + 1)]
f[0][1] = -inf
for i, p in enumerate(prices):
f[i + 1][0] = max(f[i][0], f[i][1] + p)
f[i + 1][1] = max(f[i][1], f[i][0] - p)
return f[n][0]
三、空间优化
class Solution:
def maxProfit(self, prices: List[int]) -> int:
f0, f1 = 0, -inf
for p in prices:
f0, f1 = max(f0, f1 + p), max(f1, f0 - p)
return f0
309. 最佳买卖股票时机含冷冻期
给定一个整数数组prices
,其中第 prices[i]
表示第 i
天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
- 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
一、递归搜索 + 保存计算结果 = 记忆化搜索
在 122. 买卖股票的最佳时机 II 的基础上,只需修改一处:在计算持有股票的状态时,把
dfs(i−1,0) 改成 dfs(i−2,0)。
请注意,这会导致边界条件多了一个 dfs(−2,0)=0,后面空间优化中的 pre 0指的就是这个状态。
请注意,dfs(−2,1) 是访问不到的,所以下面翻译成递推时,无需初始化这个状态(不需要写 f[0][1]=−∞)。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
@cache
def dfs(i: int, hold: bool) -> int:
if i < 0:
return -inf if hold else 0
if hold:
return max(dfs(i - 1, True), dfs(i - 2, False) - prices[i])
return max(dfs(i - 1, False), dfs(i - 1, True) + prices[i])
return dfs(n - 1, False)
二、1:1 翻译成递推
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
f = [[0] * 2 for _ in range(n + 2)]
f[1][1] = -inf
for i, p in enumerate(prices):
f[i + 2][0] = max(f[i + 1][0], f[i + 1][1] + p)
f[i + 2][1] = max(f[i + 1][1], f[i][0] - p)
return f[-1][0]
三、空间优化
class Solution:
def maxProfit(self, prices: List[int]) -> int:
pre0, f0, f1, = 0, 0, -inf
for p in prices:
pre0, f0, f1 = f0, max(f0, f1 + p), max(f1, pre0 - p)
return f0
188. 买卖股票的最佳时机 IV
给定一个整数数组 prices
,它的第 i
个元素 prices[i]
是一支给定的股票在第 i
天的价格,和一个整型 k
。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k
笔交易。也就是说,你最多可以买 k
次,卖 k
次。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
一、递归搜索 + 保存计算结果 = 记忆化搜索
在 122. 买卖股票的最佳时机 II 的基础上,添加一个参数 j,表示当前可以至多交易 j 次。
请注意:由于最后未持有股票,手上的股票一定会卖掉,所以 j-1 可以是在买股票的时候,也可以是在卖股票的时候,这两种写法都是可以的
class Solution:
def maxProfit(self, k: int, prices: List[int]) -> int:
n = len(prices)
@cache
def dfs(i: int, j: int, hold: bool) -> int:
if j < 0:
return -inf
if i < 0:
return -inf if hold else 0
if hold:
return max(dfs(i - 1, j, True), dfs(i - 1, j - 1, False) - prices[i])
return max(dfs(i - 1, j, False), dfs(i - 1, j, True) + prices[i])
return dfs(n - 1, k, False)
二、1:1 翻译成递推
class Solution:
def maxProfit(self, k: int, prices: List[int]) -> int:
n = len(prices)
f = [[[-inf] * 2 for _ in range(k + 2)] for _ in range(n + 1)]
for j in range(1, k + 2):
f[0][j][0] = 0
for i, p in enumerate(prices):
for j in range(1, k + 2):
f[i + 1][j][0] = max(f[i][j][0], f[i][j][1] + p)
f[i + 1][j][1] = max(f[i][j][1], f[i][j - 1][0] - p)
return f[-1][-1][0]
三、空间优化
class Solution:
def maxProfit(self, k: int, prices: List[int]) -> int:
f = [[-inf] * 2 for _ in range(k + 2)]
for j in range(1, k + 2):
f[j][0] = 0
for i, p in enumerate(prices):
for j in range(k + 1, 0, -1): # 原理见 0-1 背包那期视频
f[j][0] = max(f[j][0], f[j][1] + p)
f[j][1] = max(f[j][1], f[j - 1][0] - p)
return f[-1][0]
恰好/至少交易
递归到 i<0 时,只有j=0 才是合法的,j>0 是不合法的。
# 恰好
class Solution:
def maxProfit(self, k: int, prices: List[int]) -> int:
# 递推
n = len(prices)
f = [[[-inf] * 2 for _ in range(k + 2)] for _ in range(n + 1)]
f[0][1][0] = 0 # 只需改这里
for i, p in enumerate(prices):
for j in range(1, k + 2):
f[i + 1][j][0] = max(f[i][j][0], f[i][j][1] + p)
f[i + 1][j][1] = max(f[i][j][1], f[i][j - 1][0] - p)
return f[-1][-1][0]
# 记忆化搜索
# @cache
# def dfs(i: int, j: int, hold: bool) -> int:
# if j < 0:
# return -inf
# if i < 0:
# return -inf if hold or j > 0 else 0
# if hold:
# return max(dfs(i - 1, j, True), dfs(i - 1, j - 1, False) - prices[i])
# return max(dfs(i - 1, j, False), dfs(i - 1, j, True) + prices[i])
# return dfs(n - 1, k, False)
如果改成「至少」完成 k 笔交易要怎么做?递归到「至少 0 次」时,它等价于「交易次数没有限制」,那么这个状态的计算方式和 122. 买卖股票的最佳时机 II 是一样的。
# 至少
class Solution:
def maxProfit(self, k: int, prices: List[int]) -> int:
# 递推
n = len(prices)
f = [[[-inf] * 2 for _ in range(k + 1)] for _ in range(n + 1)]
f[0][0][0] = 0
for i, p in enumerate(prices):
f[i + 1][0][0] = max(f[i][0][0], f[i][0][1] + p)
f[i + 1][0][1] = max(f[i][0][1], f[i][0][0] - p) # 无限次
for j in range(1, k + 1):
f[i + 1][j][0] = max(f[i][j][0], f[i][j][1] + p)
f[i + 1][j][1] = max(f[i][j][1], f[i][j - 1][0] - p)
return f[-1][-1][0]
# 记忆化搜索
# @cache
# def dfs(i: int, j: int, hold: bool) -> int:
# if i < 0:
# return -inf if hold or j > 0 else 0
# if hold:
# return max(dfs(i - 1, j, True), dfs(i - 1, j - 1, False) - prices[i])
# return max(dfs(i - 1, j, False), dfs(i - 1, j, True) + prices[i])
# return dfs(n - 1, k, False)