问题描述
小R近期表现出色,公司决定以股票的形式给予奖励,并允许他在市场上进行交易以最大化收益。给定一个数组,数组中的第 i 个元素代表第 i 天的股票价格。小R需要设计一个算法来实现最大利润。
股票交易规则如下:
- 小R可以多次买卖股票,但在买入新的股票前必须卖出之前的股票。
- 每次卖出股票后存在一天的冷冻期,在冷冻期内小R不能购买股票。
你的任务是帮助小R计算出在遵守交易规则的情况下能够获得的最大利润。
stocks: 一个整数列表,表示连续几天内的股票价格。
测试样例
样例1:
输入:
stocks = [1, 2]
输出:1
样例2:
输入:
stocks = [2, 1]
输出:0
样例3:
输入:
stocks = [1, 2, 3, 0, 2]
输出:3
样例4:
输入:
stocks = [2, 3, 4, 5, 6, 7]
输出:5
样例5:
输入:
stocks = [1, 6, 2, 7, 13, 2, 8]
输出:12
解题思路
动态规划建模
使用动态规划解决该问题,我们将交易过程分为以下三种状态:
-
持有股票状态(hold) :在第 i 天结束时,手中仍然持有一只股票。
-
未持有股票且非冷冻期状态(rest) :在第 i 天结束时,没有持有股票,也不处于冷冻期。
-
冷冻期状态(cooldown) :在第 i 天结束时,没有持有股票,并且当天处于冷冻期。
状态定义
设:
• dp[i][0] 表示第 i 天结束时处于 hold 状态的最大利润。
• dp[i][1] 表示第 i 天结束时处于 rest 状态的最大利润。
• dp[i][2] 表示第 i 天结束时处于 cooldown 状态的最大利润。
状态转移方程
- 如果第 i 天结束时处于 hold 状态:
• 前一天也处于 hold 状态。
• 第 i 天买入股票(前一天必须是 rest 状态)。
dp[i][0] = \max(dp[i-1][0], dp[i-1][1] - stocks[i])
- 如果第 i 天结束时处于 rest 状态:
• 前一天也处于 rest 状态。
• 前一天处于 cooldown 状态。
dp[i][1] = \max(dp[i-1][1], dp[i-1][2])
- 如果第 i 天结束时处于 cooldown 状态:
• 第 i 天卖出了股票(前一天必须是 hold 状态)。
dp[i][2] = dp[i-1][0] + stocks[i]
初始化
• dp[0][0] = -stocks[0]:第 0 天持有股票的最大利润为负数(即买入股票)。
• dp[0][1] = 0:第 0 天没有持有股票且非冷冻期的利润为 0。
• dp[0][2] = 0:第 0 天没有持有股票且处于冷冻期的利润为 0。
最终结果
最后一天的最大利润是:
\max(dp[n-1][1], dp[n-1][2])
因为最终状态必须是 rest 或 cooldown 状态。
def max_profit(stocks):
n = len(stocks)
if n <= 1:
return 0
# 初始化 dp 数组
dp = [[0] * 3 for _ in range(n)]
dp[0][0] = -stocks[0] # 第一天持有股票的状态
dp[0][1] = 0 # 第一天非冷冻期的状态
dp[0][2] = 0 # 第一天冷冻期的状态
for i in range(1, n):
# 持有股票状态
dp[i][0] = max(dp[i-1][0], dp[i-1][1] - stocks[i])
# 非冷冻期状态
dp[i][1] = max(dp[i-1][1], dp[i-1][2])
# 冷冻期状态
dp[i][2] = dp[i-1][0] + stocks[i]
# 最终结果
return max(dp[n-1][1], dp[n-1][2])
# 测试样例
print(max_profit([1, 2])) # 输出:1
print(max_profit([2, 1])) # 输出:0
print(max_profit([1, 2, 3, 0, 2])) # 输出:3
print(max_profit([2, 3, 4, 5, 6, 7]))# 输出:5
print(max_profit([1, 6, 2, 7, 13, 2, 8])) # 输出:12
四、测试讲解
样例 1
输入:stocks = [1, 2]
• 第 0 天买入,第 1 天卖出。
• 利润为 2 - 1 = 1 。
输出:1
样例 2
输入:stocks = [2, 1]
• 不进行任何交易。
• 利润为 0。
输出:0
样例 3
输入:stocks = [1, 2, 3, 0, 2]
• 第 0 天买入,第 2 天卖出,利润为 3 - 1 = 2 。
• 第 3 天冷冻期,第 4 天买入并卖出,利润为 2 。
总利润为 2 + 1 = 3 。
输出:3
样例 5
输入:stocks = [1, 6, 2, 7, 13, 2, 8]
• 第 0 天买入,第 1 天卖出,利润为 6 - 1 = 5 。
• 第 2 天冷冻期,第 3 天买入,第 4 天卖出,利润为 13 - 2 = 11 。
总利润为 5 + 7 = 12 。
输出:12
五、优化方向
1. 空间优化
由于每一天的状态只与前一天相关,可以使用滚动数组将空间复杂度优化为 O(1) 。
优化代码示例:
def max_profit_optimized(stocks):
n = len(stocks)
if n <= 1:
return 0
hold, rest, cooldown = -stocks[0], 0, 0
for i in range(1, n):
prev_hold, prev_rest, prev_cooldown = hold, rest, cooldown
hold = max(prev_hold, prev_rest - stocks[i])
rest = max(prev_rest, prev_cooldown)
cooldown = prev_hold + stocks[i]
return max(rest, cooldown)