Skip to content

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

题目描述

给定一个整数数组prices,其中第 prices[i] 表示第 *i* 天的股票价格 。

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

  • 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

java
输入: prices = [1,2,3,0,2]
输出: 3 
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]

题目链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-cooldown/

文章讲解:https://programmercarl.com/0309.最佳买卖股票时机含冷冻期.html

思路

动规的题目考来考去,最关键的就是我们如何进行状态的定义。看出现了冷冻期后如何定义状态。

动规五部曲

1、dp数组及下标含义

dp[i][j] :第 i 天的状态是 j ,所剩下的最大现金是dp[i][j]。j从0到3,四个状态。

具体有四个状态:

  1. 持有股票(今天买入 或者 之前就买入了股票没有操作,一直持有)

  2. 不持有股票

    • 保持卖出状态(之前就已经卖出了,可能是前一天卖出的。也可能是两天前就卖出了,度过一天冷冻期)。为什么这个状态要单独定义呢?暂且不表
    • 今天卖出
  3. 冷冻期

2、递推公式
  1. 持有股票状态 dp[i][0],有两个具体操作:

    • 操作一:前一天就是持有股票状态(状态一),dp[i][0] = dp[i - 1][0]
    • 操作二:今天买入了,有两种情况
      • 前一天是冷冻期(状态四),dp[i - 1][3] - prices[i]
      • 前一天是保持卖出股票的状态(状态二),dp[i - 1][1] - prices[i]

    dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][3], dp[i - 1][1]) - prices[i]);

  2. 保持卖出状态,dp[i][1],两种情况

    • 操作一:前一天是卖出状态,但非冷冻期
    • 操作二:前一天是冷冻期

    dp[i][1] = max(dp[i][1],dp[i-1][3])

  3. 今天就卖出状态,dp[i][2],一种情况,前一天是持有的 dp[i][2] = dp[i-1][0] +prices[i]

  4. 冷冻期,dp[i][3],一种情况,前一天刚卖出。dp[i][3] = dp[i-1][2];至此我们回收伏笔,定义当天卖出这个特殊状态是因为冷冻期状态必须由这个状态推导而来。

C++
dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][3], dp[i - 1][1]) - prices[i]);
dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]);
dp[i][2] = dp[i - 1][0] + prices[i];
dp[i][3] = dp[i - 1][2];
3、dp数组初始化

第 0 天 持有股票dp[0][0] = -prices[0],一定是当天买入股票。

第 0 天 保持卖出状态 实际上无法定义,这种情况我们就看递推公式需要我们给他初始成什么数值。

当计算 dp[1][0] 的时候,假设 第一天要购买股票。从公式上看我们需要有dp[0][1] 。第一天持有股票,手里的现金必然是-prices[1]。那么max(dp[i - 1][3], dp[i - 1][1]) - prices[i])必然要等于 -prices[1] 这也就意味着dp[0][1] 必须为 0。或者比所有可能的已有股票价格小,我们就初始化为0

同理 dp[0][2]初始化为0,dp[0][3]也初始为0。

4、确定遍历顺序

dp[i] 依赖于 dp[i-1],所以是从前向后遍历

5、举例推导

以 [1,2,3,0,2] 为例,dp数组如下:

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

最后结果是取 状态二,状态三,和状态四的最大值,不少同学会把状态四忘了,状态四是冷冻期,最后一天如果是冷冻期也可能是最大值。

代码实现

C++
int maxProfit(vector<int>& prices) {
    int n = prices.size();
    vector<vector<int>> dp(n,vector<int>(4,0));
    dp[0][0] -= prices[0]; // 持股票
    for(int i = 1;i < n;i++){
        dp[i][0] = max(dp[i - 1][0],max(dp[i - 1][1] - prices[i],dp[i - 1][3] - prices[i]));
        dp[i][1] = max(dp[i - 1][1],dp[i - 1][3]);
        dp[i][2] = dp[i - 1][0] + prices[i];
        dp[i][3] = dp[i - 1][2];
    }
    return max(dp[n - 1][3],max(dp[n - 1][1],dp[n - 1][2]));
}