[LeetCode] 309、最佳买卖股票时机含冷冻期

题目描述

给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

  • 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
  • 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
输入: [1,2,3,0,2]
输出: 3 
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]

解题思路

“股票买卖”这个系列有好几道题,labuladong大佬有一个“一个通用方法团灭 6 道股票问题”,推荐!相似问题解题方法统一一下,不然容易做每个题都用不同的方法,总结不出套路。(动态规划三把斧

  • 动态规划

    因为本题中规定买入,卖出和冷冻操作,规定上一次卖出和下一次买入之间需要至少一天的冷冻期,因此我们定义三种状态sellbuycooldown,分别对应了到第i天为止最后一个操作是买入、卖出和冷冻所对应的最大利润(注意并不是第i天就是那个操作!例如第i-1天卖出,第i无操作,则第i天为止最后一个操作也是卖出操作sell[i]),则状态转移方程如下:(此题对状态表示的理解很重要!)

    • sell[i] = max(sell[i-1], buy[i-1] + prices[i]);
    • buy[i] = max(buy[i-1], cooldown[i-1] - prices[i]);
    • cooldown[i] = max(cooldown[i-1], sell[i-1]);

    注:如何理解第i-1天是sell状态,第i天还是sell呢?

    其实是这样的,sell[i]代表的是到第i天为止,最后一个操作是卖出操作,即昨天卖出今天无操作或者昨天买入今天卖出两种状态,所以sell[i] = max(sell[i-1], buy[i-1] + prices[i]);

    即我们可以把第i天分为确实有操作(第i天进行了买入/卖出/冷冻操作)和无操作两种状态。

参考代码

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int length = prices.size();
        if(length == 0)
            return 0;

        int buy[length], sell[length], cooldown[length];
        memset(buy, 0, sizeof(buy));  // 用vector<int> buy(length,0) 更方便
        memset(sell, 0, sizeof(sell));
        memset(cooldown, 0, sizeof(cooldown));
        buy[0] = -prices[0];  // 初始化,第一天只能执行买入操作
        for(int i = 1; i < length; i++){
            buy[i] = max(buy[i-1], cooldown[i-1] - prices[i]);
            sell[i] = max(sell[i-1], buy[i-1] + prices[i]);
            cooldown[i] = max(cooldown[i-1], sell[i-1]);
        }

        return sell[length-1];  // 最后一定是返回这个
    }
};

一个通用方法团灭 6 道股票问题

// 每次 sell 之后要等一天才能继续交易。只要把这个特点融入上一题(122、买卖股票的最佳时机 II )的状态转移方程即可:
// dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
// dp[i][1] = max(dp[i-1][1], dp[i-2][0] - prices[i])
// 解释:第 i 天选择 buy 的时候,要从第 i-2 天的状态转移,而不是 i-1 。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int length = prices.size();
        if(length <= 1)
            return 0; 

        int dp_i_0 = 0, dp_i_1 = -prices[0];
        int dp_pre_0 = 0;
        for (int i = 1; i < length; i++) {
            int temp = dp_i_0;
            dp_i_0 = max(dp_i_0, dp_i_1 + prices[i]);
            dp_i_1 = max(dp_i_1, dp_pre_0 - prices[i]);
            dp_pre_0 = temp;
        }
        
        return dp_i_0;
    }
};
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值