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

1,930 阅读4分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

国庆假期前面玩了3天,今天开始继续leetcode每日一题吧。

题目

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

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

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

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

思路

虽然只是最佳买卖股票时机题目的变形,也肯定是用动态规划,但是确实想了很久也没想出来状态的定义和转移方程。如之前的认知,动态规划的题目,最难在与定义状态,目前做了一些题目下来,发现这方面也没什么特别通用的公式或者技巧,经验比较重要一些,等再做一些题目后,就重新整理一下做过的动态规划题目的状态定义,看看是否会有什么通用的方式。本题是完全看了题解才会做的,不过我也会尽可能用自己的语言把我自己的理解写出来。
状态定义是这样的:定义一个二维数组f[][]代表最大现金收益,买入股票认为是减少现金收益,卖出股票认为是增加现金收益,那么

  • f[0][i] 当天持有1支股票
  • f[1][i] 当天不持有股票,且下一天冷冻期,即当天卖出了股票
  • f[2][i] 当天不持有股票,且下一天不是冷冻期,即当天没有做卖出 我们尝试对3种情况的f[k][i]做一些推导:
  • f[0][i]表示当天持有1支股票,那么这一支股票有2种情况:1、昨天就持有,2、今天新买入。这2种情况下,分别对应就是f[0][i] = f[0][i-1]和f[0][i] = f[2][i-1] - prices[i],所以综合起来
f[0][i] = max(f[0][i-1], f[2][i-1] - prices[i])
  • f[1][i]表示当天不持有股票,且下一天冷冻期,即当天卖出了股票。这种情况比较简单,因为当天卖出了股票,那么前一天必然是持有股票的,所以这种情况的状态转移方程就是
f[1][i] = f[0][i-1] + prices[i]
  • f[2][i]表示当天不持有股票,且下一天不是冷冻期,即当天没有做卖出。所以也有2种情况:1、昨天持有股票,且下一天冷冻期;2、昨天持有股票,且下一天不是冷冻期,综合起来
f[2][i] = max(f[1][i-1], f[2][i-1])

所以把这3中情况的状态转移方程综合起来就是

f[0][i] = max(f[0][i-1], f[2][i-1] - prices[i])
f[1][i] = f[0][i-1] + prices[i]
f[2][i] = max(f[1][i-1], f[2][i-1])

上述状态转移返程的前提条件是i>=1,那i=0呢?就是初始化的条件,我们再来看上面这3个定义:

  • f[0][i]:当天持有1支股票,那么f[0][0]就代表当天买入了股票,所以f[0][0] = -prices[0]
  • f[1][i] 当天不持有股票,且下一天冷冻期,即当天卖出了股票,这里是一个难点,因为实际上找不出1种情况来满足这个设定,不过我们可以转换表一下思路,f[1][0]可以看做当天买入且当天卖出了股票(实际不允许,但是可以这么看待),所以f[1][0] = 0
  • f[2][i] 当天不持有股票,且下一天不是冷冻期,即当天没有做卖出,所以f[2][0]可以看作当天没有任何操作,即f[2][0] = 0

这样,我们把状态转移方程和状态的初始值都搞定了,不过这类动态规划的题目很多都可以做状态压缩,观察到f[0][i]、f[1][i]、f[1][i]实际上只跟f[0][i-1]、f[1][i-1]、f[1][i-1]有关,所以,我们只要用3个变量来记录f0、f1、f2的值来记录前面一个状态值,这样可以把3个数组的状态控件压缩到3个变量。类似的还有费布那切数列求解时候的状态压缩,这里不做展开。

Java版本代码

class Solution {
    public int maxProfit(int[] prices) {
        int len = prices.length;
        if (len <= 1) {
            return 0;
        }
        int f0 = -prices[0];
        // 可以看作在第0天做了当天买卖的操作,收益就是0
        int f1 = 0;
        int f2 = 0;
        for (int i = 1; i < len; i++) {
            int temp0 = Math.max(f0, f2 - prices[i]);
            int temp1 = f0 + prices[i];
            int temp2 = Math.max(f1, f2);
            f0 = temp0;
            f1 = temp1;
            f2 = temp2;
        }
        return Math.max(f1, f2);
    }
}