LeetCode买卖股票系列总结(动态规划或者递归、进阶状态机)

题目合集

在这里插入图片描述

逐一攻破(学习模板解题)

LeetCode121. 买卖股票的最佳时机(简单)

在这里插入图片描述

使用暴力枚举的方法超时,但是能通过大部分案例,复杂度为O(n2)。
在这里插入图片描述
优化:看一下有哪些东西重叠了:
在这里插入图片描述

要想获得最大收益,那起码得买入价格>卖出的价格。那么如果我们知道了卖出的价格,只要选择在这天(可以今天买入今天卖出)前比卖出的价格小的就行了,而在当前卖出的价格中最大收益必然是来自我们买入的最小股票价格。也就是说我们维护了一个表示在第i天卖出的股票之前买入股票的最小价格curMin:
可以得到如下代码:

class Solution {
    
    
    public int maxProfit(int[] prices) {
    
    
        int res = 0;
        int curMin = prices[0];
        for(int sell=1;sell<prices.length;sell++){
    
    
            //更新当前股票的最小价格
            curMin = Math.min(curMin,prices[sell]);
            //最大的收益必然是今天卖出的和我以最低价格买入之差
            res = Math.max(res,prices[sell]-curMin);
        }
        if(res<=0) return 0;
        return res;

    }
}

时间复杂度为O(n)。那么我们就得到了一种思路:

  • 解决股票问题,先固定卖出价格,最大收益来自当前卖出价格和最低买入价格之差
    在这里插入图片描述

LeetCode122、买卖股票的最佳时机II(简单)

在这里插入图片描述
从暴力枚举的角度,我们知道我们必须先买入卖出,获得一段收益,加上剩下可用买的股票里面的收益,得到最终的总收益,然后取最大值。

  • 维护一个买入时间
  • 在这个买入时间之后暴力每个可卖出的时间,计算其最大收益:当前买入卖出的收益加上之后一段的收益,取max。
class Solution {
    
    
    public int maxProfit(int[] prices) {
    
    
        return maxProfit(prices,0);
    }
    public int maxProfit(int[]prices,int nowBuy){
    
    
        if(nowBuy==prices.length) return 0;
        int res=0;
      for(int i=nowBuy;i<prices.length;i++){
    
    //可以买入的时间
            for(int sell=i+1;sell<prices.length;sell++){
    
    //暴力枚举卖出时间
                res = Math.max(res,prices[sell]-prices[i]+maxProfit(prices,sell+1));
            }
      }
      if(res<=0) return 0;
      return res;
    }
}

提交试一波:
在这里插入图片描述
复杂度上看是O(N3),但是我们有了上一道题的经验,知道这样的做法是可优化的:

    public int maxProfit(int[]prices,int nowBuy){
    
    
        if(nowBuy==prices.length) return 0;
        int res=0;
        int curMin=prices[nowBuy];//注意初始化最小值为当前可用买入的
        for(int sell=nowBuy+1;sell<prices.length;sell++){
    
    //暴力枚举卖出时间
                curMin = Math.min(curMin,prices[sell]);
                res = Math.max(res,prices[sell]-curMin//这不就是第一题的做法+另外一段子问题吗
								+maxProfit(prices,sell+1));
            }
      if(res<=0) return 0;
      return res;
    }

结果还是超时了:
在这里插入图片描述
那我们要不要使用备忘录来记录呢?如果使用备忘录,则:

class Solution {
    
    
    public int []memo;
    public int maxProfit(int[] prices) {
    
    
        memo = new int[prices.length];
        Arrays.fill(memo,-1);
        return maxProfit(prices,0);

    }
    public int maxProfit(int[]prices,int nowBuy){
    
    
        if(nowBuy==prices.length) return 0;
        if(memo[nowBuy]!=-1) return memo[nowBuy];
        int res=0;
        int curMin=prices[nowBuy];//注意初始化最小值为当前可用买入的
        for(int sell=nowBuy+1;sell<prices.length;sell++){
    
    //暴力枚举卖出时间
                curMin = Math.min(curMin,prices[sell]);
                res = Math.max(res,prices[sell]-curMin//这不就是第一题的做法+另外一段子问题吗                                
                                +maxProfit(prices,sell+1));
            }
      if(res<=0){
    
    
          memo[nowBuy] = 0;return 0;
      }else{
    
    
          memo[nowBuy] = res;return res;
      }
    }
}

在这里插入图片描述
通过了。

但是效率很低,为什么还是简单题呢?
官方动态规划
在这里插入图片描述

class Solution {
    
    
    public int maxProfit(int[] prices) {
    
    
        int n = prices.length;
        int[][] dp = new int[n][2];
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        for (int i = 1; i < n; ++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][0] - prices[i]);
        }
        return dp[n - 1][0];
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/solution/mai-mai-gu-piao-de-zui-jia-shi-ji-ii-by-leetcode-s/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

官方贪心做法
动态规划做的基本是暴力,那么它的特殊情况就是贪心了,那么这道题是否符合贪心选择性质呢?
局部最优——》全局最优。
看官方题解:
在这里插入图片描述

class Solution {
    
    
    public int maxProfit(int[] prices) {
    
    
        int ans = 0;
        int n = prices.length;
        for (int i = 1; i < n; ++i) {
    
    
            ans += Math.max(0, prices[i] - prices[i - 1]);
        }
        return ans;
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/solution/mai-mai-gu-piao-de-zui-jia-shi-ji-ii-by-leetcode-s/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
    
    
    public int maxProfit(int[] prices) {
    
    
        int max = 0;
        for(int sell=1;sell<prices.length;sell++){
    
    
            if(prices[sell]>prices[sell-1]){
    
    
                max+= prices[sell]-prices[sell-1];
            }
        }
        return max;
    }
}

在这里插入图片描述

LeetCode123、买卖股票的最佳时机III(困难)

在这里插入图片描述

这道题,我们使用方法二中的通用模板来做:

class Solution {
    
    
    public int maxProfit(int[] prices) {
    
    
        //使用股票二的方法,但是多了一个交易次数的限制
        return maxProfit(prices,0,2);

    }
    //k控制次数少
    public int maxProfit(int []prices,int nowBuy,int k){
    
    
        if(k<=0 || nowBuy>=prices.length) return 0;//边界约束
        int res = 0;
        int curMin=prices[nowBuy];
        for(int sell=nowBuy+1;sell<prices.length;sell++){
    
    
            curMin = Math.min(prices[sell],curMin);
            res = Math.max(res,prices[sell]-curMin+maxProfit(prices,sell+1,k-1));//这里使用k-1,注意别写出了k--。
        }
        return res;

    }
}

但是超时:时间复杂度 O(k*N2)
在这里插入图片描述
使用备忘录:

class Solution {
    
    
    public int[][]memo;
    public int maxProfit(int[] prices) {
    
    
        //使用股票二的方法,但是多了一个交易次数的限制
        memo = new int[prices.length+1][2];
        for(int i=0;i<prices.length;i++){
    
    
            for(int j=0;j<2;j++){
    
    
                memo[i][j] = -1;
            }
        }
        return maxProfit(prices,0,2);

    }
    //k控制次数少
    public int maxProfit(int []prices,int nowBuy,int k){
    
    
        if(k==0 || nowBuy>=prices.length) return 0;//边界约束
        if(memo[nowBuy][k-1]!=-1) return memo[nowBuy][k-1];
        int res = 0;
        int curMin=prices[nowBuy];
        for(int sell=nowBuy+1;sell<prices.length;sell++){
    
    
            curMin = Math.min(prices[sell],curMin);
            res = Math.max(res,prices[sell]-curMin+maxProfit(prices,sell+1,k-1));//
        }
        memo[nowBuy][k-1] = res;
        return res;

    }
}

结果还是超时了:
在这里插入图片描述

老老实实看题解:
https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/solution/mai-mai-gu-piao-de-zui-jia-shi-ji-iii-by-wrnt/
在这里插入图片描述
按照自己理解的思路是很可以的。但是我觉得想出这样的法子需要很多经验。看了题解没看代码自己写了一遍觉得很清晰思路:

class Solution {
    
    
    public int[][]memo;
    public int maxProfit(int[] prices) {
    
    
       //分为5种状态,即可开始、买、卖、买、卖
       //对应buy1,sell1,buy2,sell2
       int n = prices.length;
       int buy1 = -prices[0];//第一天就买入
       int sell1 = 0;//第一天买入就卖出
       int buy2 = -prices[0];//又买入
       int sell2 = 0;
       int res = 0;
       for(int i=1;i<n;i++){
    
    
           //递推
           buy1 = Math.max(buy1,-prices[i]);//第i天为买一次,则第i-1天可为买一次,或者开始,然后第i天买入
           sell1 = Math.max(sell1,buy1+prices[i]);//前一天的状态可能为买了一次,今天卖出  或者买卖了一次
           buy2 = Math.max(buy2,sell1-prices[i]);//前一天可能就已经buy2状态,或者是sell1状态买了一次
           sell2 = Math.max(sell2,buy2+prices[i]);//前一天可能就已经sell2了,或者buy2状态卖了一次
           //最大收益可以是不完成任何交易,完成一次交易,完成两次交易
           res = Math.max(sell1,sell2);
           res = Math.max(0,res);
       }
       return res;
       }
}

在这里插入图片描述

LeetCode188、买卖股票的最佳时间IV(困难、同上面一道)

在这里插入图片描述

使用上面买卖股票的模板:

class Solution {
    
    
    public int[][]memo ;
    public int maxProfit(int k, int[] prices) {
    
    
        //类似于买卖股票的最佳时机III,只不过这里允许K次交易。
        //我们可以按照我们的常规模板来做:
        memo = new int[prices.length][k];
        return maxProfit(k,prices,0);
    }
    //k次交易,可买股票起点为nowBuy
    public int maxProfit(int k,int[]prices,int nowBuy){
    
    
        if(k<=0 || nowBuy>=prices.length) return 0;

        if(memo[nowBuy][k-1]>0){
    
    return memo[nowBuy][k-1];}//这里就不用判断0了,

        int res = 0;
        int curMin = prices[nowBuy];//当前的最小价格
        for(int sell=nowBuy+1;sell<prices.length;sell++){
    
    
            curMin = Math.min(prices[sell],curMin);//当前交易的最小买入价格
            res = Math.max(res,prices[sell]-curMin+maxProfit(k-1,prices,sell+1));
        }
        memo[nowBuy][k-1] = res;
        return res;
    }
}

然后我以为会超时,没想到啊,竟然让我通过了:
时间复杂度为O(k*n2);
在这里插入图片描述

LeetCode309、最佳买卖股票时机含冷冻期(中等)

在这里插入图片描述

明显,他们还是可以用我们的这个模板解题,只不过修改一下参数的变化而已:

class Solution {
    
    
     public int[]memo ;
    public int maxProfit(int[] prices) {
    
    
        //类似于买卖股票的最佳时机III,只不过这里允许K次交易。
        //我们可以按照我们的常规模板来做:
        memo = new int[prices.length];
        return maxProfit(prices,0);
    }
    //k次交易,可买股票起点为nowBuy
    public int maxProfit(int[]prices,int nowBuy){
    
    
        if(nowBuy>=prices.length) return 0;
        if(memo[nowBuy]>0) return memo[nowBuy];
        int res = 0;
        int curMin = prices[nowBuy];//当前的最小价格
        for(int sell=nowBuy+1;sell<prices.length;sell++){
    
    
            curMin = Math.min(prices[sell],curMin);//当前交易的最小买入价格
            //sell+1是没有冷冻期 ,sell+2则标识冷冻期1天
            res = Math.max(res,prices[sell]-curMin+maxProfit(prices,sell+2));
        }
        memo[nowBuy] = res;
        return res;
    }
}

没想到可以通过:
在这里插入图片描述

LeetCode714、买卖股票的最佳时机含手续费(中等)

在这里插入图片描述

class Solution {
    
    
     public int[]memo ;
    public int maxProfit(int[] prices,int fee) {
    
    
        //类似于买卖股票的最佳时机III,只不过这里允许K次交易。
        //我们可以按照我们的常规模板来做:
        memo = new int[prices.length];
        return maxProfit(prices,0,fee);
    }
    //k次交易,可买股票起点为nowBuy
    public int maxProfit(int[]prices,int nowBuy,int fee){
    
    
        if(nowBuy>=prices.length) return 0;
        if(memo[nowBuy]!=0) return memo[nowBuy];//这里可以改!=0
        int res = 0;
        int curMin = prices[nowBuy];//当前的最小价格
        for(int sell=nowBuy+1;sell<prices.length;sell++){
    
    
            curMin = Math.min(prices[sell],curMin);//当前交易的最小买入价格
            res = Math.max(res,prices[sell]-curMin+maxProfit(prices,sell+1,fee)-fee);
        }
        memo[nowBuy] = res;
        return res;
    }
}

超时了:
在这里插入图片描述

进阶分析(有限状态机来实现动态规划)

官方题解都是很快的算法,使用动态规划,但是看我们股票交易III就知道动态规划,考虑很多,比较精妙,我觉得我很难想得到。所以就去看labuladong的算法小炒,这种的递推需要思考状态是如何进行递推的:
参考链接:https://mp.weixin.qq.com/s?__biz=MzAxODQxMDM0Mw==&mid=2247484508&idx=1&sn=42cae6e7c5ccab1f156a83ea65b00b78&chksm=9bd7fa54aca07342d12ae149dac3dfa76dc42bcdd55df2c71e78f92dedbbcbdb36dec56ac13b&scene=21#wechat_redirect

看起来很长很复杂。但是还是得硬着去干掉这篇文章:

以LeetCode188、买卖股票的最佳时间IV来做,其他都是它的简化版本:
在这里插入图片描述

首先动态规划的角度要做什么?

  • 思考状态,题目的变量有什么?每天的股票价格,每天的交易的状态(买入、卖出、不交易)。很明显,每天股票的价格我们知道。那么我们的状态应该是——每天的交易状态
  • 定义dp数组,我们定义dp数组,它的结果必然要能够推出最后的结果,故这里三种状态应该对应该天该状态当前的最大收益。
  • 状态比较多,我们首先用简化的变量buy、sell、rest来表示三种状态,然后思考递归过程。这个时候我们用有限状态机来做
    这三个状态是否存在包含关系呢?不交易的话,我手上可以持有股票或者不持有股票,所以reset有两种可能。我们描出这两个状态:
    在这里插入图片描述
    那么状态转换是怎么样的?通过buy或者sell
    在这里插入图片描述
    此时完了吗?没有,我们还可以拥有的动作是什么也不做。
    在这里插入图片描述
    也就是说我们的状态转换就应该这个样子,如果允许K次交易,则从无股票的reset开始,逆时针转K圈即完成K次交易。上面的图也解释了我们每天本质上只有两种状态,其余的是能做的动作,使得状态转换。
  • 我们就知道目前解释状态需要的变量:交易次数、第i天、状态为reset的哪一种。我们分别使用K,i,S来表示。也就是我们的dp[i][K][S],它表示第i天状态为S,目前已经进行K次交易得到的最大利益。
  • 最终的结果是什么?dp[n-1][K][0],最后一天,最多允许 K 次交易,所能获取的最大利润,此时我必然没有股票了,因为如果有我卖出去必然有收益。

我们如何编写状态转换代码呢?
买入卖出完成一次交易。买入即可算交易数+1,卖出不算了。我们今天的状态持有股票和没持有股票。
首先考虑今天持有股票的:

dp[i][K][1] = dp[i-1][K][1] //前天有股票,今天do nothing,交易数不变
dp[i][K][1] = dp[i-1][K-1][0]-price[i]//前天无股票,今天buy,交易数+1

考虑今天不持有股票的:

dp[i][K][0]=dp[i-1][K][1]+price[i]//前天有股票,今天sell,交易数不变
dp[i][K][0]=dp[i-1][K][0]前天无股票,今天do nothing,交易数不变

状态就有了递推方程了。

之后我们思考边界:
第零天的状态:

dp[0][K][0] = 0;//第0天无法交易,无法持有股票,交易次数K无效
dp[0][K][1] = -Integer.MIN_VALUE;//第0天无法交易,持有股票不可能出现。交易次数K无效

然后递推方程:

dp[i][K][0]=Math.max(dp[i-1][K][1]+price[i]//前天有股票,今天sell,交易数不变
							,dp[i-1][K][0]前天无股票,今天do nothing,交易数不变
							)
dp[i][K][1] = Math.max(dp[i-1][K][1] //前天有股票,今天do nothing,交易数不变
					,dp[i-1][K-1][0]-price[i]//前天无股票,今天buy,交易数+1
					)

得到代码:

int [][][]dp = new int[prices.length+1][K][2];
//base case:
dp[0][K][0] = 0;//第0天无法交易,无法持有股票,交易次数K无效
dp[0][K][1] = -Integer.MIN_VALUE;//第0天无法交易,持有股票不可能出现。交易次数K无效

for(int i=1;i<=n;i++){
    
    
	for(int k=1;k<K;k++){
    
    	
		dp[i][K][0]=Math.max(dp[i-1][K][1]+price[i]//前天有股票,今天sell,交易数不变
								,dp[i-1][K][0]前天无股票,今天do nothing,交易数不变
								)
		dp[i][K][1] = Math.max(dp[i-1][K][1] //前天有股票,今天do nothing,交易数不变
						,dp[i-1][K-1][0]-price[i]//前天无股票,今天buy,交易数+1
						)
	}
}
return dp[n][K][0]

实际提交代码:

class Solution {
    
    
    public int maxProfit(int k, int[] prices) {
    
    
        int n = prices.length;
        int [][][]dp = new int[n+1][k+1][2];//i表示第i天,第i天的股票价格price[i-1]
        //base case:
        for(int K=0;K<=k;K++){
    
    
            dp[0][K][0] = 0;//第0天无法交易,无法持有股票,交易次数K无效
            dp[0][K][1] = Integer.MIN_VALUE;//第0天无法交易,持有股票不可能出现。交易次数K无效
        }
        
        for(int i=1;i<=n;i++){
    
    
            for(int K=1;K<=k;K++){
    
    	
                dp[i][K][0]=Math.max(dp[i-1][K][1]+prices[i-1]//前天有股票,今天sell,交易数不变
                                        ,dp[i-1][K][0]前天无股票,今天do nothing,交易数不变
                                        );
                dp[i][K][1] = Math.max(dp[i-1][K][1] //前天有股票,今天do nothing,交易数不变
                                ,dp[i-1][K-1][0]-prices[i-1]//前天无股票,今天buy,交易数+1
                                );
            }
        }
        return dp[n][k][0];
    }
}

在这里插入图片描述

有了这个模板,接下来我们一一攻破其他的股票题吧。

买卖股票的最佳时机III

就是只允许交易两次,把K改为2即可。

class Solution {
    
    
    public int[][]memo;
    public int maxProfit(int[] prices) {
    
    
       return maxProfit(2,prices);
       }
    public int maxProfit(int k, int[] prices) {
    
    
        int n = prices.length;
        int [][][]dp = new int[n+1][k+1][2];//i表示第i天,第i天的股票价格price[i-1]
        //base case:
        for(int K=0;K<=k;K++){
    
    
            dp[0][K][0] = 0;//第0天无法交易,无法持有股票,交易次数K无效
            dp[0][K][1] = -Integer.MIN_VALUE;//第0天无法交易,持有股票不可能出现。交易次数K无效
        }
        
        for(int i=1;i<=n;i++){
    
    
            	
                dp[i][1][0]=Math.max(dp[i-1][1][1]+prices[i-1]//前天有股票,今天sell,交易数不变
                                        ,dp[i-1][1][0]前天无股票,今天do nothing,交易数不变
                                        );
                dp[i][1][1] = Math.max(dp[i-1][1][1] //前天有股票,今天do nothing,交易数不变
                                ,dp[i-1][0][0]-prices[i-1]//前天无股票,今天buy,交易数+1
                                );
                 dp[i][2][0]=Math.max(dp[i-1][2][1]+prices[i-1]//前天有股票,今天sell,交易数不变
                                        ,dp[i-1][2][0]前天无股票,今天do nothing,交易数不变
                                        );
                dp[i][2][1] = Math.max(dp[i-1][2][1] //前天有股票,今天do nothing,交易数不变
                                ,dp[i-1][1][0]-prices[i-1]//前天无股票,今天buy,交易数+1
                                );
        }
        return dp[n][k][0];
    }
}

在这里插入图片描述
那为什么官方题解这么简洁呢?因为K=2状态太少了,我们直接就可以列举出来:
官方代码:

    public int maxProfit(int[] prices) {
    
    
       //分为5种状态,即可开始、买、卖、买、卖
       //对应buy1,sell1,buy2,sell2
       int n = prices.length;
       int buy1 = -prices[0];//第一天就买入
       int sell1 = 0;//第一天买入就卖出
       int buy2 = -prices[0];//又买入
       int sell2 = 0;
       int res = 0;
       for(int i=1;i<n;i++){
    
    
           //递推
           buy1 = Math.max(buy1,-prices[i]);//第i天为买一次,则第i-1天可为买一次,或者开始,然后第i天买入
           sell1 = Math.max(sell1,buy1+prices[i]);//前一天的状态可能为买了一次,今天卖出  或者买卖了一次
           buy2 = Math.max(buy2,sell1-prices[i]);//前一天可能就已经buy2状态,或者是sell1状态买了一次
           sell2 = Math.max(sell2,buy2+prices[i]);//前一天可能就已经sell2了,或者buy2状态卖了一次
           //最大收益可以是不完成任何交易,完成一次交易,完成两次交易
           res = Math.max(sell1,sell2);
           res = Math.max(0,res);
       }
       return res;
       }

简单的比对一下,有点累了:
在这里插入图片描述
最佳买卖股票时机(手续费)
这里无限次交易,所以我们把K这个维度去除即可。然后添加上手续费:

class Solution {
    
    
     public int[]memo ;
    public int maxProfit(int[] prices,int fee) {
    
    
        int n = prices.length;
        int [][]dp = new int[n+1][2];//i表示第i天,第i天的股票价格price[i-1]
        //base case:
        dp[0][0] = 0;//第0天无法交易,无法持有股票,交易次数K无效
        dp[0][1] = Integer.MIN_VALUE;//第0天无法交易,持有股票不可能出现。交易次数K无效
        for(int i=1;i<=n;i++){
    
    	
                dp[i][0]=Math.max(dp[i-1][1]+prices[i-1]//前天有股票,今天sell,交易数不变
                                        ,dp[i-1][0]前天无股票,今天do nothing,交易数不变
                                        );
                dp[i][1] = Math.max(dp[i-1][1] //前天有股票,今天do nothing,交易数不变
                                ,dp[i-1][0]-prices[i-1]-fee//前天无股票,今天buy,交易数+1,同时要交手续费
                                );
        }
        return dp[n][0];
    }

}

在这里插入图片描述

很明显,只和两个状态有关,可以进行压缩:

class Solution {
    
    
     public int[]memo ;
    public int maxProfit(int[] prices,int fee) {
    
    
        int n = prices.length;
       
        //base case:
        int dp_0 = 0;//第0天无法交易,无法持有股票,交易次数K无效
        int dp_1 = Integer.MIN_VALUE;//第0天无法交易,持有股票不可能出现。交易次数K无效
        for(int i=1;i<=n;i++){
    
    	
                dp_0=Math.max(dp_1+prices[i-1]//前天有股票,今天sell,交易数不变
                                        ,dp_0前天无股票,今天do nothing,交易数不变
                                        );
                dp_1 = Math.max(dp_1 //前天有股票,今天do nothing,交易数不变
                                ,dp_0-prices[i-1]-fee//前天无股票,今天buy,交易数+1,同时要交手续费
                                );
        }
        return dp_0;
    }

}

在这里插入图片描述
最佳买卖股票时机(含冷冻期)
也就是把i的下标变换稍微改一下:

在这里插入图片描述
但是这样会越位,因为我们第1天i=1,i-2 = 0;所以我们需要稍微处理一下,把前两天的结果记录下来。

class Solution {
    
    
    public int maxProfit(int[] prices) {
    
    
        int n = prices.length;
        //base case:
            // dp[0][0] = 0;//第0天无法交易,无法持有股票,交易次数K无效
            // dp[0][1] = Integer.MIN_VALUE;//第0天无法交易,持有股票不可能出现。交易次数K无效
        int dp_0 = 0;
        int dp_1 = Integer.MIN_VALUE;
        int dp_pre_0 = 0;//这个防止越界,添加的
        
        for(int i=1;i<=n;i++){
    
    
                int temp = dp_0;
                dp_0=Math.max(dp_1+prices[i-1]//前天有股票,今天sell,交易数不变
                                        ,dp_0前天无股票,今天do nothing,交易数不变
                                        );
                dp_1 = Math.max(dp_1 //前天有股票,今天do nothing,交易数不变
                                ,dp_pre_0-prices[i-1]//前天无股票,今天buy,交易数+1--这里。。第i天选择buy需要从i-2递推
                                );
                dp_pre_0 = temp;//sell出的需要冷却期

        }
        return dp_0;
    }
}

在这里插入图片描述
现在股票的题都通过了。
在这里插入图片描述
常规模板和动态规划的模板也get到了

模板总结:

常规暴力的简化
买卖股票1和2可以总结:

class Solution {
    
    
    public int []memo;
    public int maxProfit(int[] prices) {
    
    
        memo = new int[prices.length];
        Arrays.fill(memo,-1);
        return maxProfit(prices,0);

    }
    public int maxProfit(int[]prices,int nowBuy){
    
    
        if(nowBuy==prices.length) return 0;
        if(memo[nowBuy]!=-1) return memo[nowBuy];
        int res=0;
        int curMin=prices[nowBuy];//注意初始化最小值为当前可用买入的
        for(int sell=nowBuy+1;sell<prices.length;sell++){
    
    //暴力枚举卖出时间
                curMin = Math.min(curMin,prices[sell]);
                res = Math.max(res,prices[sell]-curMin//这不就是第一题的做法+另外一段子问题吗                                
                                +maxProfit(prices,sell+1));
            }
      if(res<=0){
    
    
          memo[nowBuy] = 0;return 0;
      }else{
    
    
          memo[nowBuy] = res;return res;
      }
    }
}

动态规划
买卖股票IV可以总结:

class Solution {
    
    
    public int maxProfit(int k, int[] prices) {
    
    
        int n = prices.length;
        int [][][]dp = new int[n+1][k+1][2];//i表示第i天,第i天的股票价格price[i-1]
        //base case:
        for(int K=0;K<=k;K++){
    
    
            dp[0][K][0] = 0;//第0天无法交易,无法持有股票,交易次数K无效
            dp[0][K][1] = Integer.MIN_VALUE;//第0天无法交易,持有股票不可能出现。交易次数K无效
        }
        
        for(int i=1;i<=n;i++){
    
    
            for(int K=1;K<=k;K++){
    
    	
                dp[i][K][0]=Math.max(dp[i-1][K][1]+prices[i-1]//前天有股票,今天sell,交易数不变
                                        ,dp[i-1][K][0]前天无股票,今天do nothing,交易数不变
                                        );
                dp[i][K][1] = Math.max(dp[i-1][K][1] //前天有股票,今天do nothing,交易数不变
                                ,dp[i-1][K-1][0]-prices[i-1]//前天无股票,今天buy,交易数+1
                                );
            }
        }
        return dp[n][k][0];
    }
}

猜你喜欢

转载自blog.csdn.net/qq_44861675/article/details/115261558