跳至主要內容

0309. 最佳买卖股票时机含冷冻期

ITCharge大约 4 分钟

0309. 最佳买卖股票时机含冷冻期open in new window

  • 标签:数组、动态规划
  • 难度:中等

题目链接

题目大意

给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

  • 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
  • 卖出股票后,你无法在第二天买入股票(即冷冻期为 1 天)。

解题思路

这道题是「0122. 买卖股票的最佳时机 IIopen in new window」的升级版。

冷冻期的意思是:如果昨天卖出了,那么今天不能买。在考虑的时候只要判断一下前一天是不是刚卖出。

对于每一天结束时的状态总共有以下几种:

  • 买入状态:
    • 今日买入
    • 之前买入,之后一直持有无操作
  • 卖出状态:
    • 今日买出,正处于冷冻期
    • 昨天卖出,今天结束后度过了冷冻期
    • 之前卖出,度过了冷冻期后无操作

在买入状态中,今日买入和之前买入的状态其实可以看做是股票的持有状态,可以将其合并为一种状态。

在卖出状态中,昨天卖出和之前卖出的状态其实可以看做是无股票并度过了冷冻期状态,可以将其合并为一种状态。

这样总结下来可以划分为三个状态:

  • 股票的持有状态。
  • 无股票,并且处于冷冻期状态。
  • 无股票,并且不处于冷冻期状态。

所以我们可以定义状态 dp[i][j] ,表示为:第 i 天第 j 种情况(0 <= j <= 2)下,所获取的最大利润。

注意:这里第第 j 种情况,

接下来确定状态转移公式:

  • 0 种状态(股票的持有状态)下可以有两种状态推出,取最大的那一种赋值:
    • 昨天就已经持有的:dp[i][0] = dp[i - 1][0]
    • 今天刚买入的(则昨天不能持有股票也不能处于冷冻期,应来自于前天卖出状态):dp[i][0] = dp[i - 1][2] - prices[i]
  • 1 种状态(无股票,并且处于冷冻期状态)下可以有一种状态推出:
    • 今天卖出:dp[i] = dp[i - 1][0] + prices[i]
  • 2 种状态(无股票,并且不处于冷冻期状态)下可以有两种状态推出,取最大的那一种赋值:
    • 昨天卖出:dp[i] = dp[i - 1][1]
    • 之前卖出:dp[i] = dp[i - 1][2]

下面确定初始化的边界值:

可以很明显看出第一天不做任何操作就是 dp[0][0] = 0,第一次买入就是 dp[0][1] = -prices[i]

第一次卖出的话,可以视作为没有盈利(当天买卖,价格没有变化),即 dp[0][2] = 0。第二次买入的话,就是 dp[0][3] = -prices[i]。同理第二次卖出就是 dp[0][4] = 0

在递推结束后,最大利润肯定是无操作、第一次卖出、第二次卖出这三种情况里边,且为最大值。我们在维护的时候维护的是最大值,则第一次卖出、第二次卖出所获得的利润肯定大于等于 0。而且,如果最优情况为一笔交易,那么在转移状态时,我们允许在一天内进行两次交易,则一笔交易的状态可以转移至两笔交易。所以最终答案为 dp[size - 1][4]size 为股票天数。

代码

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        size = len(prices)
        if size == 0:
            return 0
        dp = [[0 for _ in range(4)] for _ in range(size)]

        dp[0][0] = -prices[0]
        for i in range(1, size):
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][2] - prices[i])
            dp[i][1] = dp[i - 1][0] + prices[i]
            dp[i][2] = max(dp[i - 1][1], dp[i - 1][2])
        return max(dp[size - 1][0], dp[size - 1][1], dp[size - 1][2])