leecode-123-买卖股票的最佳时机

题目描述

在这里插入图片描述
在这里插入图片描述

思路

官方题解给的是使用动态规划的方法求解,这里也只总结一下动态规划的方法。我也只能想到用暴力解法解决了,但是参考意义不大,所以这里也就不记录了。
以下思路参考官方题解

动态规划

因为题目要求最多可以完成两笔交易,因此在任意一天结束之后,我们都将处于下面五种状态之一:

  • 未进行过任何操作
  • 只进行过一次买操作
  • 进行了一次买操作和一次卖操作,即完成了一笔交易
  • 进行了第二次买操作
  • 完成了全部两笔交易
    对于状态1 很明显利润为0,这个我们就不进行记录了
    状态2的最大利润记为 buy1
    状态3的最大利润记为 sell1
    状态4的最大利润记为buy2
    状态5的最大利润记为sell2

为了便于理解,我们暂且将这四个状态分别用数组来表示,索引对应天数。
下面分析第n天能够获得的最大利润

buy1[n] 表示第n天只买了一支股票,那么
buy1[n] = max{buy1[n-1],-prices[n]}
这表示第n天如果只买了一支股票,那么它的最大值要么是第n-1天买一只股票的最大值,要么是前n-1天都没买,买第n天的股票

sell1[n]表示第n天结束后买卖完成了第一支股票(这并不代表这支股票是在第n天卖出的)
sell1[n] = max{sell1[n-1],prices[n] + buy1[n-1]}
sell1[n-1]表示在第n天没有操作(即不进行卖第一支股票),prices[n] + buy1[n-1]表示在第n天卖出第一支股票

buy2[n]表示在第n天结束后已经买了第二支股票(在第一支股票交易完成的基础上买了第二支,且并不代表一定是在这一天买的第二支)
buy2[n] = max{buy2[n-1],sell1[n-1]-prices[n]}

sell2[n]表示在第n天结束后已经卖出了第二支股票
sell2[n] = max{sell2[n-1],buy2[n-1] + prices[n]}

在考虑边界条件时,我们需要理解下面的这个事实:
无论题目中是否允许【在同一天买入并且卖出】这一操作,最终的答案都不会受影响,因为这一操作带来的收益为0

因此在状态转移时,我们可以换种写法:
例如在计算sell1[n]时,我们直接使用buy1[n],而不是buy1[n-1]进行转移。
sell1[n] = max{sell1[n-1],prices[n] + buy1[n]}

buy1[n] 比 buy1[n-1] 多考虑的是在第n天买入股票的情况,而转移到sell1时,考虑的是在第n天卖出股票的情况,这样在同一天买入并且卖出收益为零,不会对答案产生影响。同理对于buy2以及sell2,我们同样可以直接根据第n天计算出的值来进行转移。

上面是官方题解给的解释,我们可以将他的理论类比到buy2,sell2上面
buy2:sell1[n] 比 sell1[n-1] 多考虑了在第n天卖出股票的情况,即在sell1[n-1]的基础上加上 prices[n] 。转移到 buy2上面,buy2[n] 比 buy2[n-1] 多考虑了买下第n天的股票,即利润 -prices[n] ,跟上面的sell1[n]多考虑的抵消掉了。

sell2:跟sell1的解释相同。

有了这个理论,四个状态变量就可以只使用四个 int 类型就可以了
在这里插入图片描述
初始第 0 天的四个状态
buy1 = -prices[0];
sell1 = 0;
buy2 = -prices[0];
sell2 = 0;

然后进行动态规划。
我们发现,最终的结果一定保存在 0,sell1,sell2三个之中的最大值。

由于在边界条件中sell1 和 sell2的值已经为0,并且在状态转移的过程总维护的已经是最大值,因此sell1和sell2最终一定大于等于0。同时,如果最优的情况对应的是只买一支股票,即保存在sell1中,那么它也会因为我们在转移时允许在同一天买入并且卖出这一宽松的条件,从sell1转移至sell2,所以最终的答案即为sell2。

代码:

class Solution {
    public int maxProfit(int[] prices) {
        //动态规划
        //根据题解的思路:
        //第i天过去后我们都处于下面五种状态的其中一种:
        /**
            1. 还没有进行过交易
            2. 买了第一支股票(还没卖出去)
            3. 卖了第一支股票
            4. 买了第二支股票
            5. 卖了第二支股票
        */
        //对于第1个状态的最大利润显然是 0
        //第2个状态的最大利润记为 buy1
        //第3个状态的最大利润记为 sell1
        //第4个状态的最大利润记为 buy2
        //第5个状态的最大利润即为 sell2

        /**
            第一天:
                buy1 = -prices[0];
                sell1 = 0;
                buy2 = -prices[0];
                sell2 = 0;
            第i天:
                buy1 = max{buy1`,-prices[i]} 
                sell1 = max{sell1,prices[i] + buy1`}
                buy2 = max{buy2`,sell1 - prices[i]}
                sell2 = max{sell2,prices[i] + buy2`}
        */
        int n = prices.length;
        int buy1 = -prices[0];
        int sell1 = 0;
        int buy2 = -prices[0];
        int sell2 = 0;
        for(int i=1;i<n;i++){
            buy1 = Math.max(buy1,-prices[i]);
            sell1 = Math.max(sell1,prices[i] + buy1);
            buy2 = Math.max(buy2,sell1 - prices[i]);
            sell2 = Math.max(sell2,prices[i] + buy2);
        }

        return sell2;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值