股票交易系列
leetcode相关题目链接:
121. 买卖股票的最佳时机
122. 买卖股票的最佳时机 II
123. 买卖股票的最佳时机 III
188. 买卖股票的最佳时机 IV
309. 买卖股票的最佳时机含冷冻期
714. 买卖股票的最佳时机含手续费
买卖股票终极问题:
买卖股票的最佳时机含手续费、冷冻期和交易次数的限制!!
买卖股票问题
三个状态,分别是:天数、允许交易的最大次数、当前的持有状态【可以用1表示持有,用0表示未持有】
dp[i][k][0 or 1]
0 <= i <= n-1, n为天数
1 <= k <= K , 大K为交易次数上限
此类问题共 n × K × 2 种状态,全部穷举就能搞定。
一次买入和一次卖出 计为一次完整的交易。
因为交易是从buy开始的,所以在buy的时候给 k 减小 1。若在sell的时候给 k 减小 1,在
buy的时候不改变交易次数 k 的话,会出现交易次数超出限制的错误!! 切记。
** base case **
dp[-1][..][0] = dp[..][0][0] = 0
dp[-1][..][1] = dp[..][0][1] = -inf
** 状态转移方程 **
# 分别对应 第 i-1天持有然后第 i 天 卖出、第 i-1天未持有然后第 i 天 不操作两种情况。
dp[i][k][0] = max(dp[i-1][k][1] + prices[i], dp[i-1][k][0])
# 分别对应 第 i-1天未持有然后第 i 天 买入、第 i-1天持有然后第 i 天 不操作两种情况。
dp[i][k][1] = max(dp[i-1][k-1][0] - prices[i], dp[i-1][k][1])
class MaxProfit:
"""
买卖股票问题
三个状态,分别是:天数、允许交易的最大次数、当前的持有状态【可以用1表示持有,用0表示未持有】
dp[i][k][0 or 1]
0 <= i <= n-1, n为天数
1 <= k <= K , 大K为交易次数上限
此类问题共 n × K × 2 种状态,全部穷举就能搞定。
一次买入和一次卖出 计为一次完整的交易。
因为交易是从buy开始的,所以在buy的时候给 k 减小 1。若在sell的时候给 k 减小 1,在
buy的时候不改变交易次数 k 的话,会出现交易次数超出限制的错误!! 切记。
** base case **
dp[-1][..][0] = dp[..][0][0] = 0
dp[-1][..][1] = dp[..][0][1] = -inf
** 状态转移方程 **
# 分别对应 第 i-1天持有然后第 i 天 卖出、第 i-1天未持有然后第 i 天 不操作两种情况。
dp[i][k][0] = max(dp[i-1][k][1] + prices[i], dp[i-1][k][0])
# 分别对应 第 i-1天未持有然后第 i 天 买入、第 i-1天持有然后第 i 天 不操作两种情况。
dp[i][k][1] = max(dp[i-1][k-1][0] - prices[i], dp[i-1][k][1])
"""
def solution(self, prices: List[int]) -> int:
"""
121. 买卖股票的最佳时机
这里相当于只交易一次, k = 1
https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/description/
:param prices:
:return:
"""
n = len(prices)
dp = [[0] * 2 for _ in range(n)]
for i in range(n):
# base case
if i == 0:
dp[i][0] = 0
dp[i][1] = -prices[i]
continue
dp[i][0] = max(dp[i-1][1] + prices[i], dp[i-1][0])
dp[i][1] = max(-prices[i], dp[i-1][1])
return dp[n-1][0]
def solution2(self, prices: List[int]) -> int:
"""
121. 买卖股票的最佳时机【空间复杂度优化】
这里相当于只交易一次, k = 1
https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/description/
:param prices:
:return:
"""
n = len(prices)
# base case
dp_i_0 = 0
dp_i_1 = float('-inf')
for i in range(n):
dp_i_0 = max(dp_i_1 + prices[i], dp_i_0)
dp_i_1 = max(-prices[i], dp_i_1)
return dp_i_0
def solution3(self, prices: List[int]) -> int:
"""
122. 买卖股票的最佳时机 II
https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/description/
这里相当于可交易无穷多次, k = inf,那就可以认为k和k-1是一样的。
dp[i][k][0] = max(dp[i-1][k][1] + prices[i], dp[i-1][k][0])
dp[i][k][1] = max(dp[i-1][k][0] - prices[i], dp[i-1][k][1])
发现dp数组中的k已经不会改变了,不需要记录k这个状态了,状态转移方程为:
dp[i][0] = max(dp[i-1][1] + prices[i], dp[i-1][0])
dp[i][1] = max(dp[i-1][0] - prices[i], dp[i-1][1])
:param prices:
:return:
"""
n = len(prices)
dp = [[0] * 2 for _ in range(n)]
for i in range(n):
# base case
if i == 0:
dp[i][0] = 0
dp[i][1] = -prices[i]
continue
dp[i][0] = max(dp[i - 1][1] + prices[i], dp[i - 1][0])
dp[i][1] = max(dp[i - 1][0] - prices[i], dp[i - 1][1])
return dp[n - 1][0]
def solution4(self, prices: List[int]) -> int:
"""
122. 买卖股票的最佳时机 II 【空间复杂度优化】
https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/description/
"""
n = len(prices)
# base case
dp_i_0 = 0
dp_i_1 = float('-inf')
for i in range(n):
dp_i_0 = max(dp_i_1 + prices[i], dp_i_0)
dp_i_1 = max(dp_i_0 - prices[i], dp_i_1)
return dp_i_0
def solution5(self, prices: List[int]) -> int:
"""
309. 买卖股票的最佳时机含冷冻期
https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-cooldown/
:param prices:
:return:
"""
n = len(prices)
dp = [[0] * 2 for _ in range(n)]
for i in range(n):
# base case 1
if i == 0:
dp[i][0] = 0
dp[i][1] = -prices[i]
continue
# base case 2
if i == 1:
dp[i][0] = max(dp[0][1] + prices[i], dp[0][0])
dp[i][1] = max(-prices[i], dp[0][1])
continue
dp[i][0] = max(dp[i - 1][1] + prices[i], dp[i - 1][0])
# 第 i 天选择 buy 的时候,要从 i-2 的状态转移,⽽不是 i-1 。
dp[i][1] = max(dp[i - 2][0] - prices[i], dp[i - 1][1])
return dp[n - 1][0]
def solution6(self, prices: List[int]) -> int:
"""
309. 买卖股票的最佳时机含冷冻期 【空间复杂度优化】
https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-cooldown/
:param prices:
:return:
"""
n = len(prices)
# base case
dp_i_0 = 0
dp_i_1 = float('-inf')
dp_pre_0 = 0 # 表示 dp[i - 2][0]
for i in range(n):
temp = dp_i_0
dp_i_0 = max(dp_i_1 + prices[i], dp_i_0)
dp_i_1 = max(dp_pre_0 - prices[i], dp_i_1)
dp_pre_0 = temp
return dp_i_0
def solution7(self, prices: List[int], fee: int) -> int:
"""
714. 买卖股票的最佳时机含手续费
https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/
:param prices:
:return:
"""
n = len(prices)
dp = [[0] * 2 for _ in range(n)]
for i in range(n):
# base case 1
if i == 0:
dp[i][0] = 0
dp[i][1] = -prices[i] - fee
continue
dp[i][0] = max(dp[i - 1][1] + prices[i], dp[i - 1][0])
# 买入的时候增加交易费,相当于于买⼊股票的价格升⾼了。
dp[i][1] = max(dp[i - 1][0] - prices[i] - fee, dp[i - 1][1])
return dp[n - 1][0]
def solution8(self, prices: List[int], fee: int) -> int:
"""
714. 买卖股票的最佳时机含手续费 【空间复杂度优化】
https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/
:param prices:
:return:
"""
n = len(prices)
# base case
dp_i_0 = 0
dp_i_1 = float('-inf')
for i in range(n):
dp_i_0 = max(dp_i_1 + prices[i], dp_i_0)
dp_i_1 = max(dp_i_0 - prices[i] - fee, dp_i_1)
return dp_i_0
def solution9(self, prices: List[int]) -> int:
"""
123. 买卖股票的最佳时机 III
最多可以完成 两笔 交易。
https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/ :param prices:
:return:
"""
n = len(prices)
max_k = 2
dp = [[[0] * 2 for _ in range(max_k+1)] for _ in range(n)]
for i in range(n):
for k in range(max_k, 0, -1):
# base case
if i == 0:
dp[i][k][0] = 0
dp[i][k][1] = -prices[i]
continue
dp[i][k][0] = max(dp[i-1][k][1] + prices[i], dp[i-1][k][0])
# 买入股票,交易次数变更
dp[i][k][1] = max(dp[i-1][k-1][0] - prices[i], dp[i-1][k][1])
return dp[n-1][max_k][0]
def solution10(self, prices: List[int]) -> int:
"""
123. 买卖股票的最佳时机 III
最多可以完成 两笔 交易。
https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/ :param prices:
:return:
"""
dp_i10, dp_i20 = 0, 0
dp_i11, dp_i21 = float('-inf'), float('-inf')
for price in prices:
dp_i11 = max(dp_i11, -price)
dp_i10 = max(dp_i10, dp_i11 + price)
dp_i21 = max(dp_i21, dp_i10 - price)
dp_i20 = max(dp_i20, dp_i21 + price)
return dp_i20
def solution11(self, k: int, prices: List[int]) -> int:
"""
188. 买卖股票的最佳时机 IV
最多可以完成 k笔 交易。
https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iv/ :return:
"""
n = len(prices)
max_k = k
dp = [[[0] * 2 for _ in range(max_k+1)] for _ in range(n)]
# k=0 时的 base case
for i in range(n):
dp[i][0][0] = 0
dp[i][0][1] = float('-inf')
for i in range(n):
for k in range(max_k, 0, -1):
# base case
if i == 0:
dp[i][k][0] = 0
dp[i][k][1] = -prices[i]
continue
dp[i][k][0] = max(dp[i-1][k][1] + prices[i], dp[i-1][k][0])
# 买入股票,交易次数变更
dp[i][k][1] = max(dp[i-1][k-1][0] - prices[i], dp[i-1][k][1])
return dp[n-1][max_k][0]
def max_profit_all_in_one(self, max_k, prices, cooldown, fee):
"""
终极股票交易问题【同时考虑交易次数的限制、冷冻期和⼿续费】
输⼊股票价格数组 prices,你最多进⾏ max_k 次交易,每次交易需要额外消耗 fee 的⼿续费,
⽽且每次交易之后需要经过 cooldown 天的冷冻期才能进⾏下⼀次交易,请你计算并返回可以获得的最⼤利润。
:param max_k:
:param prices:
:param cooldown:
:param fee:
:return:
"""
n = len(prices)
if n <= 0:
return 0
if max_k > n // 2:
return self.max_profit_k_inf(prices, cooldown, fee)
dp = [[[0] * 2 for _ in range(max_k + 1)] for _ in range(n)]
# k=0 时的base case
for i in range(n):
dp[i][0][1] = float('-inf')
dp[i][0][0] = 0
for i in range(n):
for k in range(max_k, 0, -1):
# base case
if i - 1 == -1:
dp[i][k][0] = 0
dp[i][k][1] = -prices[i] - fee
continue
if i - cooldown - 1 < 0:
dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i])
dp[i][k][1] = max(dp[i - 1][k][1], -prices[i] - fee)
continue
dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i])
dp[i][k][1] = max(dp[i - 1][k][1], dp[i - cooldown - 1][k - 1][0] - prices[i] - fee)
return dp[n - 1][max_k][0]
def max_profit_k_inf(self, prices, cooldown, fee):
"""
k ⽆限制,包含⼿续费和冷冻期
:param prices:
:param cooldown:
:param fee:
:return:
"""
n = len(prices)
dp = [[0] * 2 for _ in range(n)]
for i in range(n):
if i - 1 == -1:
dp[i][0] = 0
dp[i][1] = -prices[i] - fee
continue
if i - cooldown - 1 < 0:
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i])
dp[i][1] = max(dp[i - 1][1], -prices[i] - fee)
continue
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i])
dp[i][1] = max(dp[i - 1][1], dp[i - cooldown - 1][0] - prices[i] - fee)
return dp[n - 1][0]