80. 股票市场交易策略优化题解 | 豆包MarsCode AI刷题

63 阅读4分钟

问题解析

我们需要帮助小R计算在遵守股票交易规则的情况下的最大利润。交易规则如下:

  1. 可以买多次:允许多次买入和卖出。
  2. 必须先卖后买:在买入新的股票之前,必须先卖出之前持有的股票。
  3. 冷冻期限制:每次卖出股票后,需要经过一天的冷冻期才能再次购买。

这是一个典型的动态规划问题,问题可以通过状态转移方程来解决。

思路解析

动态规划状态定义

我们需要定义几个状态来描述每天的情况:

  1. dp[i][0]:第 i 天不持有股票,且 没有处于冷冻期 的最大利润。
  2. dp[i][1]:第 i 天持有股票的最大利润。
  3. dp[i][2]:第 i 天不持有股票,且 处于冷冻期 的最大利润。

状态转移方程

根据每种状态的可能操作,我们可以定义以下状态转移方程:

  1. dp[i][0]:不持有股票,且不在冷冻期

    • i 天什么都不做,继承前一天 dp[i-1][0]
    • i 天结束冷冻期,从 dp[i-1][2] 继承。
    • 状态方程: dp[i][0]=max(dp[i1][0],dp[i1][2])dp[i][0]=max⁡(dp[i−1][0],dp[i−1][2])
  2. dp[i][1]:持有股票

    • i 天什么都不做,继承前一天 dp[i-1][1]
    • i 天买入股票,从 dp[i-1][0] 中扣除股票价格。
    • 状态方程: dp[i][1]=max(dp[i1][1],dp[i1][0]stocks[i])dp[i][1]=max⁡(dp[i−1][1],dp[i−1][0]−stocks[i])
  3. dp[i][2]:不持有股票,且处于冷冻期

    • i 天卖出股票,从 dp[i-1][1] 中加上股票价格。
    • 状态方程: dp[i][2]=dp[i1][1]+stocks[i]dp[i][2]dp[i][2]=dp[i−1][1]+stocks[i]dp[i][2]

dp数组初始化

  • 第 0 天:

    • dp[0][0] = 0:未持有股票,且没有交易。
    • dp[0][1] = -stocks[0]:买入第一天的股票。
    • dp[0][2] = 0:不可能在第 0 天处于冷冻期。

最终答案

最终的最大利润是:

max(dp[n1][0],dp[n1][2])max⁡(dp[n−1][0],dp[n−1][2])

因为最后一天不可能持有股票时利润最大。

代码实现

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])

复杂度分析

  1. 时间复杂度O(n)
  2. 空间复杂度O(n)

如果优化空间,只需记录前一天的状态即可,复杂度为 O(1)

优化方法

在原版代码中,dp[i][0], dp[i][1], dp[i][2] 的值只与 dp[i-1][...] 有关。因此,可以用滚动变量代替数组,分别记录:

  • dp_0:未持有股票,且不在冷冻期。
  • dp_1:持有股票。
  • dp_2:未持有股票,且处于冷冻期。

通过更新这三个变量的值,实现状态转移。

状态转移解析

状态转移方程

与原版的状态方程一致:

  1. 未持有,无冷冻期(dp_0

    dp0=max(之前的 dp0,之前的 dp2)dp_0=max⁡(之前的 dp_0,之前的 dp_2)

    • 如果今天什么都不做,取昨天未持有且无冷冻期的最大利润(dp_0)。
    • 如果今天结束冷冻期,取昨天冷冻期结束后的利润(dp_2)。
  2. 持有股票(dp_1

    dp1=max(之前的 dp1,之前的 dp0stocks[i])dp_1=max⁡(之前的 dp_1,之前的 dp_0−stocks[i])

    • 如果今天什么都不做,继承昨天持有股票的利润(dp_1)。
    • 如果今天买入股票,利润为昨天未持有股票(dp_0)减去今天的股票价格。
  3. 未持有,冷冻期(dp_2

    dp2=之前的 dp1+stocks[i]dp_2=之前的 dp_1+stocks[i]

    • 如果今天卖出股票,利润为昨天持有股票的利润(dp_1)加上今天的股票价格。

初始化条件

  1. 第 0 天未持有股票(无冷冻期):dp_0 = 0
  2. 第 0 天持有股票:dp_1 = -stocks[0](因为买入股票)。
  3. 第 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)