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

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

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

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

示例:

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

一、思路

先来说说这道题目的难点:

  • 冷冻期

如何处理冷冻期是关键,一般来说,会使用动态规划来计算此类问题。

(一)动态规划

按照之前的思路,我使用两个动态规划表来做,结果发现行不通:

我们考虑两种情况:

  • 不在冷冻期内购买(dp1)。
  • 在冷冻期内购买(dp2),此时,出售日期需要提前一天,也就是说虽然本次交易的利润并非最大利润,但是我们将赌注压在了冷冻期上。

于是代码逻辑就清晰,我们设置2个数组:

  • dp1[i]:交易到第i天为止的最大利润
  • dp2[i]:交易到第i天为止,且明天仍然能够购买股票,所能获取的最大利润

但是这个思路有个很大的限制,就是明天购买的问题,对于不同的区间,明天购买这个操作所能带来的利润是不一样的,我们没办法通过计算一个一个的小区间来

最后参考了一下解题,写的很好:一个方法团灭 6 道股票问题

上面说的是一个泛化模型,这里针对这道题目,详细展开一下。

1、状态转移框架

这里先建立一个动态规划表 d p [ i ] [ j ] dp[i][j] dp[i][j]

  • i:表示天数,第i天
  • j:表示现在持有股票的状态,0表示没有持有股票,1表示持有股票

动态规划表 d p [ i ] [ j ] dp[i][j] dp[i][j]的意义就很明显了,就在第i天时,手里持有股票(j=1)或者没有持股(j=0)所能获取的最大利润。

除此之外,在这一天,还有3种操作可选:buy、sell、rest。

我们现在要做的就是遍历所有可能的状态,求出利润最大的,那么这里的解题伪代码可以写成:

for 0 <= i < n:
	for j in {0, 1}:
		dp[i][j] = max{buy, sell, rest}

这是状态转移图:
在这里插入图片描述
于是所求的答案为: d p [ n − 1 ] [ 0 ] dp[n - 1][0] dp[n1][0]

2、状态转移方程
  • d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] ) dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]) dp[i][0]=max(dp[i1][0],dp[i1][1]+prices[i])
    这个方程表达的意思是:在第i天,手里没有持股,所能获取的最大利润为 d p [ i ] [ 0 ] dp[i][0] dp[i][0]
    那么这个利润是怎么来的?这要从当前的状态分析,因为当前没有持股,要么昨天没有持股,要么昨天持股,但是今天卖掉了。
  • d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] − p r i c e s [ i ] ) dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]) dp[i][1]=max(dp[i1][1],dp[i1][0]prices[i])
    这个方程表达的意思是:在第i天,手里持股,所能获取的最大利润为 d p [ i ] [ 1 ] dp[i][1] dp[i][1]
    那么这个利润是怎么来的?这要从当前的状态分析,因为当前持股,要么昨天持股,要么昨天没有持股,但是今天买入了股票。这个方程存在一个小小的问题。

这里注意一下,因为股票卖出后,有一天的冷冻期不能购买,这里对 d p [ i − 1 ] [ 0 ] − p r i c e s [ i ] dp[i - 1][0] - prices[i] dp[i1][0]prices[i]操作是否合法,也需要分析。这就涉及到 d p [ i − 1 ] [ 0 ] dp[i - 1][0] dp[i1][0]是通过哪种方法得到的,代入公式替换之得:
m a x ( d p [ i − 2 ] [ 0 ] , d p [ i − 2 ] [ 1 ] + p r i c e s [ i − 1 ] ) − p r i c e s [ i ] max(dp[i - 2][0], dp[i - 2][1] + prices[i - 1]) - prices[i] max(dp[i2][0],dp[i2][1]+prices[i1])prices[i]

显然只有 d p [ i − 2 ] [ 0 ] dp[i - 2][0] dp[i2][0]的情况下,该操作才合法,于是我们可以直接将 d p [ i − 1 ] [ 0 ] dp[i - 1][0] dp[i1][0]替换为 d p [ i − 2 ] [ 0 ] dp[i - 2][0] dp[i2][0]来确保操作一定是合法的。

于是修改第二个动态规划方程:

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

实际上这两个方程可以这么解释:

  • 更新持股利润时等价于:在已有的最大利润基础上,尽可能的挑选低价股票购买
  • 更新不持股利润时等价于:在已有的最大利润基础上,尽可能的将股票以高价卖出。

C++代码:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size() == 0 || prices.size() == 1)
            return 0;
        vector<vector<int>> dp(prices.size(), vector<int>(2, 0));
        // 初始化
        dp[0][1] = -prices[0];
        dp[1][1] = (-prices[0] > -prices[1]) ? -prices[0] : -prices[1];
        dp[1][0] = max(dp[0][0], dp[0][1] + prices[1]);
        // 动态规划
        for(int i=2; i < prices.size(); i++){
            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]);
        }
        return dp[prices.size() - 1][0];
    }
};

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值