动态规划之股票收益最大化 | 豆包MarsCode AI刷题

105 阅读4分钟

image.png

需要解决的是一个带冷冻期的股票买卖问题。具体来说:

  • 小R可以多次买卖股票,但必须遵守以下规则:

    1. 在买入股票之前,必须卖出之前的股票。
    2. 每次卖出股票后必须有一天的冷冻期,在冷冻期内不能进行任何买入操作。

目标是最大化利润,即尽可能通过买卖股票获取更高的收益。

代码结构和思路

代码实现了一个动态规划的方法,核心思想是维护三个变量来表示在每一天的不同状态下能够获得的最大利润。

1. 状态定义

定义三个变量来表示三种状态:

  • hold:表示第 i 天持有股票的最大利润。
  • sold:表示第 i 天卖出股票后的最大利润。
  • rest:表示第 i 天没有持有股票并且不处于冷冻期的最大利润。

2. 初始化

  • hold:在第一天买入股票时,hold 的利润是负的,因为买入股票会支付费用,即 hold = -stocks[0]
  • sold:在第一天没有卖出任何股票,因此 sold = 0
  • rest:在第一天没有持有股票且没有进行任何买卖,因此 rest = 0

3. 状态转移

使用一个循环,从第二天开始(即 i = 1),逐步更新每个状态:

  • hold[i] :表示第 i 天持有股票的最大利润。可以有两种可能:

    1. 继续持有股票:如果前一天持有股票,那么今天不卖股票,hold[i] = hold[i-1]
    2. 今天买入股票:如果前一天没有持股票且不处于冷冻期,则可以今天买入股票,hold[i] = rest[i-1] - stocks[i]
  • sold[i] :表示第 i 天卖出股票后的最大利润。可以有两种可能:

    1. 前一天就卖出:如果前一天已经持有股票并在今天卖出,则利润是前一天持有股票的利润加上今天卖出股票的收益,sold[i] = hold[i-1] + stocks[i]
  • rest[i] :表示第 i 天没有持股票且不处于冷冻期的最大利润。可以有两种可能:

    1. 前一天没有持股:即 rest[i] = rest[i-1]
    2. 前一天处于冷冻期:即 rest[i] = sold[i-1]。冷冻期结束后,今天可以恢复到不持股且不处于冷冻期的状态。

4. 结果输出

最终,最大利润是在最后一天的两种可能状态中取最大值:

  • sold:表示最后一天卖出股票后的最大利润。
  • rest:表示最后一天没有持股票且不处于冷冻期的最大利润。

所以返回 max(sold, rest)

代码分析

def solution(stocks):
    # 如果股票价格列表为空,返回0
    if len(stocks) == 0:
        return 0

    n = len(stocks)
    
    # 初始化持有股票、卖出股票和不持股状态
    hold = -stocks[0]  # 第一天买入股票,利润为负值
    sold = 0            # 第一天没有卖出股票
    rest = 0            # 第一天不持股且不处于冷冻期

    # 从第2天开始,逐天更新状态
    for i in range(1, n):
        pre_hold = hold   # 记录前一天的持有股票状态
        pre_sold = sold   # 记录前一天的卖出股票状态
        pre_rest = rest   # 记录前一天的无股票且不在冷冻期状态

        # 更新今天的持有股票状态
        hold = max(pre_hold, pre_rest - stocks[i])

        # 更新今天的卖出股票状态
        sold = pre_hold + stocks[i]

        # 更新今天的不持有股票且不在冷冻期状态
        rest = max(pre_rest, pre_sold)

    # 返回最后的最大利润,可能是在冷冻期后或者没有持股且不处于冷冻期
    return max(sold, rest)

逐行解析

  1. 检查输入的股票价格数组是否为空

    if len(stocks) == 0:
         return 0
    

    如果没有股票数据,返回 0

  2. 初始化状态变量

    hold = -stocks[0]
    sold = 0
    rest = 0
    
    • hold = -stocks[0]:第一天买入股票,持股的最大利润为负的股票价格。
    • sold = 0:没有进行任何卖出交易,所以利润为0。
    • rest = 0:没有持股且不在冷冻期,利润为0。
  3. 循环更新每一天的状态

    for i in range(1, n):
         pre_hold = hold
         pre_sold = sold
         pre_rest = rest
    

    保存前一天的状态,以便更新当前状态。

  4. 更新 holdsoldrest

    hold = max(pre_hold, pre_rest - stocks[i])
    sold = pre_hold + stocks[i]
    rest = max(pre_rest, pre_sold)
    
    • hold = max(pre_hold, pre_rest - stocks[i]):持有股票的最大利润。可以选择继续持股或者今天买入股票。
    • sold = pre_hold + stocks[i]:卖出股票后的最大利润。
    • rest = max(pre_rest, pre_sold):不持股票且不在冷冻期的最大利润。
  5. 返回最终最大利润

    return max(sold, rest)
    

    最后一天,最大利润可能是 soldrest,返回二者的较大值。

时间复杂度与空间复杂度

  • 时间复杂度O(n),因为我们遍历了股票数组一次,并且每次计算都只需要常数时间。
  • 空间复杂度O(1),因为我们只使用了三个常量变量 holdsoldrest,没有使用额外的空间来存储数组。