问题解析
我们需要帮助小R计算在遵守股票交易规则的情况下的最大利润。交易规则如下:
- 可以买多次:允许多次买入和卖出。
- 必须先卖后买:在买入新的股票之前,必须先卖出之前持有的股票。
- 冷冻期限制:每次卖出股票后,需要经过一天的冷冻期才能再次购买。
这是一个典型的动态规划问题,问题可以通过状态转移方程来解决。
思路解析
动态规划状态定义
我们需要定义几个状态来描述每天的情况:
dp[i][0]:第i天不持有股票,且 没有处于冷冻期 的最大利润。dp[i][1]:第i天持有股票的最大利润。dp[i][2]:第i天不持有股票,且 处于冷冻期 的最大利润。
状态转移方程
根据每种状态的可能操作,我们可以定义以下状态转移方程:
-
dp[i][0]:不持有股票,且不在冷冻期- 第
i天什么都不做,继承前一天dp[i-1][0]。 - 第
i天结束冷冻期,从dp[i-1][2]继承。 - 状态方程:
- 第
-
dp[i][1]:持有股票- 第
i天什么都不做,继承前一天dp[i-1][1]。 - 第
i天买入股票,从dp[i-1][0]中扣除股票价格。 - 状态方程:
- 第
-
dp[i][2]:不持有股票,且处于冷冻期- 第
i天卖出股票,从dp[i-1][1]中加上股票价格。 - 状态方程:
- 第
dp数组初始化
-
第 0 天:
dp[0][0] = 0:未持有股票,且没有交易。dp[0][1] = -stocks[0]:买入第一天的股票。dp[0][2] = 0:不可能在第 0 天处于冷冻期。
最终答案
最终的最大利润是:
因为最后一天不可能持有股票时利润最大。
代码实现
def solution(stocks):
if not stocks:
return 0
n = len(stocks)
dp = [[0] * 3 for _ in range(n)]
# 初始化
dp[0][0] = 0
dp[0][1] = -stocks[0]
dp[0][2] = 0
for i in range(1, n):
# 第 i 天的状态转移
dp[i][0] = max(dp[i-1][0], dp[i-1][2]) # 不持有,无冷冻期
dp[i][1] = max(dp[i-1][1], dp[i-1][0] - stocks[i]) # 持有股票
dp[i][2] = dp[i-1][1] + stocks[i] # 不持有,冷冻期
# 最终结果
return max(dp[n-1][0], dp[n-1][2])
复杂度分析
- 时间复杂度:
O(n)。 - 空间复杂度:
O(n)。
如果优化空间,只需记录前一天的状态即可,复杂度为 O(1)。
优化方法
在原版代码中,dp[i][0], dp[i][1], dp[i][2] 的值只与 dp[i-1][...] 有关。因此,可以用滚动变量代替数组,分别记录:
dp_0:未持有股票,且不在冷冻期。dp_1:持有股票。dp_2:未持有股票,且处于冷冻期。
通过更新这三个变量的值,实现状态转移。
状态转移解析
状态转移方程
与原版的状态方程一致:
-
未持有,无冷冻期(
dp_0) :- 如果今天什么都不做,取昨天未持有且无冷冻期的最大利润(
dp_0)。 - 如果今天结束冷冻期,取昨天冷冻期结束后的利润(
dp_2)。
- 如果今天什么都不做,取昨天未持有且无冷冻期的最大利润(
-
持有股票(
dp_1) :- 如果今天什么都不做,继承昨天持有股票的利润(
dp_1)。 - 如果今天买入股票,利润为昨天未持有股票(
dp_0)减去今天的股票价格。
- 如果今天什么都不做,继承昨天持有股票的利润(
-
未持有,冷冻期(
dp_2) :- 如果今天卖出股票,利润为昨天持有股票的利润(
dp_1)加上今天的股票价格。
- 如果今天卖出股票,利润为昨天持有股票的利润(
初始化条件
- 第 0 天未持有股票(无冷冻期):
dp_0 = 0。 - 第 0 天持有股票:
dp_1 = -stocks[0](因为买入股票)。 - 第 0 天未持有股票(冷冻期):
dp_2 = 0(不可能处于冷冻期)。
优化版代码
def solution(stocks):
if not stocks:
return 0
# 初始化三个状态
dp_0, dp_1, dp_2 = 0, -stocks[0], 0
for i in range(1, len(stocks)):
new_dp_0 = max(dp_0, dp_2) # 未持有,无冷冻期
new_dp_1 = max(dp_1, dp_0 - stocks[i]) # 持有股票
new_dp_2 = dp_1 + stocks[i] # 冷冻期
dp_0, dp_1, dp_2 = new_dp_0, new_dp_1, new_dp_2
return max(dp_0, dp_2)