【每日一题】买卖股票的最佳时机含冷冻期

Tag

【动态规划】【数组】【2023-10-05】


题目来源

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


题目解读

这是股票系列问题的第五篇了,要求求出买卖股票的最佳时期以获得最大的利润,本题的关键在于:你卖出股票后无法在当天买入股票,需要等待一天。


解题思路

方法一:动态规划+空间优化

状态

我们使用 f[i] 表示第 i 天结束后的累计最大收益。因为我们最多同时买入一只股票并且有股票冷冻期的限制,所以会有以下三种不同的状态:

  • 我们目前持有一只股票,对应的累计最大收益用 f[i][0] 表示;
  • 我们目前不持有任何一只股票,并处于冷冻期中,对应的累计最大收益用 f[i][1] 表示;
  • 我们目前不持有任何一只股票,不处于冷冻期中,对应的累计最大收益用 f[i][2] 表示。

转移关系

对于以上的三种状态,我们有这样的转移关系:

  • 对于 f[i][0] 即我们第 i 天持有一只股票,这可以是第 i-1 天手中就持有的,对用的状态为 f[i-1][0];或者是第 i 天买入的,那么第 i-1 就不能持有股票并且不处于冷冻期中,对用的状态为 f[i-1][2],此时还要扣除买 prices[i] 股票的费用。因此,该情况下的状态转移方程为:

f [ i ] [ 0 ] = m a x ( f [ i − 1 ] [ 0 ] , f [ i − 1 ] [ 2 ] − p r i c e s [ i ] ) f[i][0] = max(f[i-1][0], f[i-1][2] - prices[i]) f[i][0]=max(f[i1][0],f[i1][2]prices[i])

  • 对于 f[i][1] 即我们第 i 天不持有任何一只股票且处于冷冻期,说明我们第 i 天卖出了一只股票,也就说明第 i-1 天我们必须持有一只股票,对应的状态为 f[i-1][0] 加上股票卖出的收益。因此,此时的转移关系为:

f [ i ] [ 1 ] = f [ i − 1 ] [ 0 ] + p r i c e s [ i ] f[i][1] = f[i-1][0] + prices[i] f[i][1]=f[i1][0]+prices[i]

  • 对于 f[i][2] 即我们第 i 天不持有任何一只股票,不处于冷冻期中,说明当天没有进行任何操作,那么第 i-1 天也就不会持有任何股票;第 i-1 天不持有任何一只股票又会有两种状态。因此,此时的转移关系为:

f [ i ] [ 2 ] = m a x ( f [ i − 1 ] [ 1 ] , f [ i − 1 ] [ 1 ] ) f[i][2] = max(f[i-1][1], f[i-1][1]) f[i][2]=max(f[i1][1],f[i1][1])

base case

f[0][0] = -prices[0],第 0 天持有股票,那么只能在第 0 天买入;
f[0][1] = 0f[0][2] = 0,我们不持有股票,那么收益为 0

最后返回

最后返回的是 max(f[n-1][0], f[n-1][1], f[n-1][2]),即第 n-1 天交易结束后三种状态的最大值,但是我们知道卖光手中的股票一定比手中还留有股票可以获得更大的利润,因此最后返回 max(f[n-1][1], f[n-1][2])

实现代码

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        vector<vector<int>> f(n, vector<int>(3, 0));
        f[0][0] = -prices[0];
        for (int i = 1; i < n; ++i) {
            f[i][0] = max(f[i-1][0], f[i-1][2] - prices[i]);
            f[i][1] = f[i-1][0] + prices[i];
            f[i][2] = max(f[i-1][1], f[i-1][2]);
        }
        return max(f[n-1][1], f[n-1][2]);
    }
};

空间优化

换一种想法,我们要通过买卖股票获得最大的利润,那么我们肯定希望手中的钱越多越好。我们仅使用三个变量来维护三种交易状态后的手中剩余的最大钱数:

  • buy:一天交易结束后手中还有股票的最大收益,初始化为 -prices[0]
  • sell:一天交易结束后手中没有股票的最大收益,,初始化为 0
  • sell_pre:用来记录前一天卖出股票后手中剩余的最大钱数,初始为 0;如果没有冷冻期,那么本题就退化成 122. 买卖股票的最佳时机 II,此时我们可以在第 i 天卖了股票之后又买当天的股票。但是,在增加了冷冻期的本题中,卖了股票之后就不能再买当天的股票了,需要等到第二天才能买,因此需要 sell_pre

注意对 sell_pre 的理解。

实现代码

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int buy = -prices[0];
        int sell_pre = 0, sell = 0;
        for (int p : prices) {
            buy = max(buy, sell_pre - p);
            sell_pre = sell;
            sell = max(sell, buy + p);
        }
        return sell;
    }
};

复杂度分析

时间复杂度: O ( n ) O(n) O(n) n n n 为数组 prices 的长度。

空间复杂度: O ( n ) O(n) O(n),使用的额外空间为记录每天交易结束后的状态,占用的空间为 O ( 3 × n ) O(3 \times n) O(3×n)。空间优化后的空间复杂度为 O ( 1 ) O(1) O(1)


写在最后

买卖股票系列题目

题目解答
121. 买卖股票的最佳时机【面试经典150】买卖股票的最佳时机
122. 买卖股票的最佳时机 II【面试经典150】买卖股票的最佳时机 II
123. 买卖股票的最佳时机 III【每日一题】买卖股票的最佳时机 III
188. 买卖股票的最佳时机 IV【每日一题】买卖股票的最佳时机 IV
714. 买卖股票的最佳时机含手续费【每日一题】买卖股票的最佳时机含手续费

如果文章内容有任何错误或者您对文章有任何疑问,欢迎私信博主或者在评论区指出 💬💬💬。

如果大家有更优的时间、空间复杂度方法,欢迎评论区交流。

最后,感谢您的阅读,如果感到有所收获的话可以给博主点一个 👍 哦。

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wang_nn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值