2021-01-09 121./123.买卖股票的最佳时机[动态规划]

121.买卖股票的最佳时机

最多1笔交易

class Solution {
    
    
    public int maxProfit(int[] prices) {
    
    
        // 我们使用一个变量专门记录到[0,i]中的最小值
        if(prices.length==0)
            return 0;
        int minPrice = prices[0];
        int profit = 0; //用来记录[0,i]天我们获得的最大利润,我们不希望亏本,所以选择0作为初始值
        for(int i=1;i<prices.length;i++){
    
    
            if(prices[i]<minPrice)
                minPrice=prices[i];
            profit = Math.max(profit,prices[i]-minPrice);
        }
        return profit;
    }
}

123.买卖股票的最佳时机

  • 最多2笔交易。
  • 相似题目:
    121,122,123

思路一:连续贪心2次(错误)

这里的思路是参考121的思路,我们在第一次贪心完的左右侧在进行贪心。
反例:
[6,1,3,2,4,7]
错误的解答:
(7-1)+0=6
正确答案:
(3-1)+(7-2)=7

class Solution {
    
    
    // 找到[start,end[间获利最多的办法 -> 121题
    // 返回最大利润,startIndex,endIndex 表示第startIndex天买入,第endIndex天卖出
    public int[] maxProfit(int[] prices,int start,int end) {
    
    
        int[] res = new int[3]; //为了便于理解,我们就最后赋值

        if(start==end || start<0 || end>prices.length)
            return res;

        int  startIndex=start,endIndex=start,minIndex=start;
        int minPrice = prices[start];
        
        int profit = 0; //用来记录[0,i]天我们获得的最大利润,我们不希望亏本,所以选择0作为初始值
        for(int i=start+1;i<end;i++){
    
    
            if(prices[i]<minPrice){
    
    
                minPrice=prices[i];
                minIndex=i;  //minPrice记录的是第minIndex天的价格
            }
            if(profit<prices[i]-minPrice){
    
    
                profit = prices[i]-minPrice;
                startIndex=minIndex;
                endIndex = i;
            }
        }

        res[0]=profit;
        res[1]=startIndex;
        res[2]=endIndex;
        return res;
    }
    public int maxProfit(int[] prices) {
    
    
        int[] invest1,invest2; //用于记录最后两个投资
        int[] investl,investr;
        
        invest1 = maxProfit(prices,0,prices.length);
        System.out.println("invest1 "+invest1[0]+" "+invest1[1]+" "+invest1[2]+" ");

        investl = maxProfit(prices,0,invest1[1]+1);  //如果可以买入又卖出,即第i天先卖再买入
        investr = maxProfit(prices,invest1[2],prices.length);
        System.out.println("investl "+investl[0]+" "+investl[1]+" "+investl[2]+" ");
        System.out.println("investr "+investr[0]+" "+investr[1]+" "+investr[2]+" ");

        return invest1[0]+Math.max(investl[0],investr[0]);
    }
}

思路二:动态规划

首先,我们给出问题中所有可能的状态:

  1. 未进行任何操作
  2. 进行过1次买的操作
  3. 进行过1次买和卖的操作
  4. 在完成了一笔交易的基础上,进行了第二次买的操作。
  5. 完成了2笔买卖

并且我们发现状态 i i i只能到状态 i + 1 i+1 i+1或状态 i i i,不存在跳跃的情况。

接着,我们考虑各个状态所能获得的最大利润,分别记为:

  • 0 (因为没有完成任何交易)
  • b u y 1 buy_1 buy1
  • s e l l 1 sell_1 sell1
  • b u y 2 buy_2 buy2
  • s e l l 2 sell_2 sell2

然后,我们希望给出每个“时刻”这些变量的值,即状态转移方程

  • 状态一:0 ∀ i \forall i i
  • 状态二: b u y 1 [ i ] = m a x ( b u y 1 [ i − 1 ] , − p r i c e [ i ] ) buy_1[i] = max(buy_1[i-1],-price[i]) buy1[i]=max(buy1[i1],price[i]),即我们是在[0,i-1]中某一天买,还是第i天买,目的就是在最便宜的时候买进。
  • 状态三: s e l l 1 [ i ] = m a x ( s e l l 1 [ i − 1 ] , b u y 1 [ i − 1 ] + p r i c e [ i ] ) sell_1[i]=max(sell_1[i-1],buy_1[i-1]+price[i]) sell1[i]=max(sell1[i1],buy1[i1]+price[i]),即我们是在第[i-1]天卖掉,还是第i天卖掉。
  • 状态四: b u y 2 [ i ] = m a x ( b u y 2 [ i − 1 ] , s e l l 1 [ i − 1 ] − p r i c e [ i ] ) buy_2[i]=max(buy_2[i-1],sell_1[i-1]-price[i]) buy2[i]=max(buy2[i1],sell1[i1]price[i])
  • 状态五: s e l l 2 [ i ] = m a x ( s e l l 2 [ i − 1 ] , s e l l 2 [ i − 1 ] + p r i c e [ i ] ) sell_2[i]=max(sell_2[i-1],sell_2[i-1]+price[i]) sell2[i]=max(sell2[i1],sell2[i1]+price[i])

因此我们的状态转移方程为:
{ b u y 1 = m a x { b u y 1 , − p r i c e s [ i ] } s e l l 1 = m a x { s e l l 1 , b u y 1 + p r i c e s [ i ] } b u y 2 = m a x { s e l l 1 , s e l l 1 − p r i c e s [ i ] } s e l l 2 = m a x { s e l l 2 , b u y 2 + p r i c e s [ i ] } \left\{\begin{matrix} buy_1 = max\{buy_1,-prices[i]\} \\ sell_1 = max\{sell_1,buy_1+prices[i]\} \\ buy_2 = max\{sell_1,sell_1-prices[i]\} \\ sell_2 = max\{sell_2,buy_2+prices[i]\} \end{matrix}\right. buy1=max{ buy1,prices[i]}sell1=max{ sell1,buy1+prices[i]}buy2=max{ sell1,sell1prices[i]}sell2=max{ sell2,buy2+prices[i]}
初始条件:
{ b u y 1 = − p r i c e s [ 0 ] s e l l 1 = 0 b u y 2 = − p r i c e s [ 0 ] s e l l 2 = 0 \left\{\begin{matrix} buy_1 = -prices[0] \\ sell_1 = 0 \\ buy_2 = -prices[0] \\ sell_2 = 0 \end{matrix}\right. buy1=prices[0]sell1=0buy2=prices[0]sell2=0

最后看看我们返回哪个值,
鉴于 s e l l 1 , s e l l 2 sell_1,sell_2 sell1,sell2的初始值就是0了,它们之后的值都一定比0要大,所以我们不用考虑状态1的0了。
此时,我们考虑的就是,到最后返回的到底是1次交易还是2次交易?
实际上,我们可以把1次交易看成2次交易,即在buy1卖出的那天同时进行买卖操作。这样sell2的值就会和sell1重合了。
因此我们只需要返回sell2即可

class Solution {
    
    
    public int maxProfit(int[] prices) {
    
    
        if(prices.length==0)
            return 0;

        int buy1=-prices[0],sell1=0,buy2=-prices[0],sell2=0;
        for(int i=0;i<prices.length;i++){
    
    
            buy1 = Math.max(buy1,-prices[i]);
            sell1 = Math.max(sell1,buy1+prices[i]);
            buy2 = Math.max(buy2,sell1-prices[i]);
            sell2 = Math.max(sell2,buy2+prices[i]);
        }
        return sell2;
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_44495738/article/details/112409089