0买股票最佳时机中等 LeetCode309. 最佳买卖股票时机含冷冻

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

描述

给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。​
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

分析

来自leetcode的题解:非状态机的DP讲解,全新思路,超通俗易懂,包你一遍看懂

前言:不要关注冷冻期!不要关注冷冻期!不要关注冷冻期!
只关注卖出的那一天!只关注卖出的那一天!只关注卖出的那一天!
题目中定义的“冷冻期”=卖出的那一天的后一天,题目设置冷冻期的意思是,如果昨天卖出了,今天不可买入,那么关键在于哪一天卖出,只要在今天想买入的时候判断一下前一天是不是刚卖出,即可,所以关键的一天其实是卖出的那一天,而不是卖出的后一天
正文:
因为当天卖出股票实际上也是属于“不持有”的状态,那么第i天如果不持有,那这个“不持有”就有了两种状态:1.本来就不持有,指不是因为当天卖出了才不持有的;2.第i天因为卖出了股票才变得不持有
而持有股票依旧只有一种状态

所以对于每一天i,都有可能是三种状态:
0.不持股且当天没卖出,定义其最大收益dp[i][0];
1.持股,定义其最大收益dp[i][1];
2.不持股且当天卖出了,定义其最大收益dp[i][2];

初始化:
dp[0][0]=0;//本来就不持有,啥也没干
dp[0][1]=-1*prices[0];//第0天只买入
dp[0][2]=0;//可以理解成第0天买入又卖出,那么第0天就是“不持股且当天卖出了”这个状态了,其收益为0,所以初始化为0是合理的

重头戏:
一、第i天不持股且没卖出的状态dp[i][0],也就是我没有股票,而且还不是因为我卖了它才没有的,那换句话说是从i-1天到第i天转移时,它压根就没给我股票!所以i-1天一定也是不持有,那就是不持有的两种可能:i-1天不持股且当天没有卖出dp[i-1][0];i-1天不持股但是当天卖出去了dp[i-1][2];
所以: dp[i][0]=max(dp[i-1][0],dp[i-1][2])

二、第i天持股dp[i][1],今天我持股,来自两种可能:
1、要么是昨天我就持股,今天继承昨天的,也就是dp[i-1][1],这种可能很好理解;
2、要么:是昨天我不持股,今天我买入的,但前提是昨天我一定没卖!因为如果昨天我卖了,那么今天我不能交易!也就是题目中所谓“冷冻期”的含义,只有昨天是“不持股且当天没卖出”这个状态,我今天才能买入!所以是dp[i-1][0]-p[i]
所以: dp[i][1]=max(dp[i-1][1],dp[i-1][0]-p[i])

三、i天不持股且当天卖出了,这种就简单了,那就是说昨天我一定是持股的,要不然我今天拿什么卖啊,而持股只有一种状态,昨天持股的收益加上今天卖出得到的新收益,就是dp[i-1][1]+p[i]啦
所以:dp[i][2]=dp[i-1][1]+p[i]

总结:最后一天的最大收益有两种可能,而且一定是“不持有”状态下的两种可能,把这两种“不持有”比较一下大小,返回即可
作者:jin-ai-yi
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/solution/fei-zhuang-tai-ji-de-dpjiang-jie-chao-ji-tong-su-y/

以下分析来自程序员Carl

  • dp[i][0]

状态0:买入股票状态(今天买入股票,或者是之前就买入了股票然后没有操作)
卖出股票状态,这里就有两种卖出股票状态
状态1:两天前就卖出了股票,度过了冷冻期,一直没操作,今天保持卖出股票状态
状态2:今天卖出了股票
状态3:今天为冷冻期状态,但冷冻期状态不可持续,只有一天

  • 动态转移方程
  • 达到买入股票状态(状态一)即:dp[i][0],有两个具体操作:
    操作一:前一天就是持有股票状态(状态一),dp[i][0] = dp[i - 1][0]
    操作二:今天买入了,有两种情况
    前一天是冷冻期(状态四)
    前一天是保持卖出股票状态(状态二)
  • 达到保持卖出股票状态(状态二)即:dp[i][1],有两个具体操作:
    操作一:前一天就是状态二
    操作二:前一天是冷冻期(状态四)
    dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]);
  • 达到今天就卖出股票状态(状态三),即:dp[i][2] ,只有一个操作:
    操作一:昨天一定是买入股票状态(状态一),今天卖出
    即:dp[i][2] = dp[i - 1][0] + prices[i];
  • 达到冷冻期状态(状态四),即:dp[i][3],只有一个操作:
    操作一:昨天卖出了股票(状态三)
    p[i][3] = dp[i - 1][2];
  • dp数组如何初始化

这里主要讨论一下第0天如何初始化。
如果是持有股票状态(状态一)那么:dp[0][0] = -prices[0],买入股票所省现金为负数。
保持卖出股票状态(状态二),第0天没有卖出dp[0][1]初始化为0就行,
今天卖出了股票(状态三),同样dp[0][2]初始化为0,因为最少收益就是0,绝不会是负数。
同理dp[0][3]也初始为0。

按照第一种思路的代码

class Solution {
    public int maxProfit(int[] prices) {
        int[][] dp = new int[prices.length][3];
        if(dp.length <= 1){
            return 0;
        }
        dp[0][0] = -prices[0];
        dp[0][1] = 0;//未持股且不是当天卖掉的
        dp[0][2] = 0;//未持股且是当天卖掉的
        for(int i = 1; i < prices.length; i++){
            dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]-prices[i]);
            dp[i][1] = Math.max(dp[i-1][1],dp[i-1][2]);
            dp[i][2] = dp[i-1][0]+prices[i];
        }
        return Math.max(dp[prices.length-1][1],dp[prices.length-1][2]);
    }
}

代码是按照“程序员Carl”的思路实现的

class Solution {
    public int maxProfit(int[] prices) {
        int[][] dp = new int[prices.length][4];
        dp[0][0] = -prices[0];
        dp[0][1] = 0;
        dp[0][2] = 0;
        dp[0][3] = 0;
        for(int i = 1; i < prices.length; i++){
            dp[i][0] = Math.max(dp[i-1][0],Math.max(dp[i-1][1],dp[i-1][3]) - prices[i]);
            dp[i][1] = Math.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];
        }
        int a = Math.max(dp[prices.length-1][2],dp[prices.length-1][3]);
        return Math.max(a,dp[prices.length-1][1]);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值