LeetCode 123-买卖股票的最佳时机III

在这里插入图片描述

动态规划

思路与算法

由于我们最多可以完成两笔交易,因此在任意一条结束之后,我们会处于以下五个状态中的一种:

  • 未进行过任何操作;
  • 只进行过一次买操作;
  • 进行了一次买操作和一次卖操作,即完成了一笔交易;
  • 在完成了一笔交易的前提下,进行了第二次买操作;
  • 完成了全部两笔交易。

由于第一个状态的利润显然为 0 0 0,因此我们可以不用将其记录。对于剩下的四个状态,我们分别将它们的最大利润记为: b u y 1 、 s e l l 1 、 b u y 2 、 s e l l 2 buy_1、sell_1、buy_2、sell_2 buy1sell1buy2sell2

如果我们知道了第 i − 1 i-1 i1 天结束后的这四个状态,那么如何通过状态转移方程得到第 i i i 天结束后的这四个状态呢?

  • 对于 b u y 1 buy_1 buy1 而言,在第 i i i 天我们可以不进行任何操作,保持不变,也可以在未进行任何操作的前提下以 p r i c e s [ i ] prices[i] prices[i] 的价格买入股票,那么 buy 1 \textit{buy}_1 buy1 的状态转移方程即为: b u y 1 = max ⁡ { b u y 1 ′ , − p r i c e s [ i ] } buy_1=\max\{buy'_1, -prices[i]\} buy1=max{buy1,prices[i]}
    这里,我们用 b u y 1 ′ buy'_1 buy1 表示第 i − 1 i-1 i1 天的状态,以便于和第 i i i 天的状态 b u y 1 buy_1 buy1 进行区分。

  • 对于 s e l l 1 sell_1 sell1 而言,在第 i i i 天我们可以不进行任何操作,保持不变,也可以在只进行过一次买操作的前提下以 p r i c e s [ i ] prices[i] prices[i] 的价格卖出股票,那么 s e l l 1 sell_1 sell1 的状态转移方程为: s e l l 1 = max ⁡ { s e l l 1 ′ , b u y 1 ′ + p r i c e s [ i ] } sell_1=\max\{sell'_1,buy'_1+prices[i]\} sell1=max{sell1,buy1+prices[i]}

  • 同理我们可以得到 b u y 2 buy_2 buy2 s e l l 2 sell_2 sell2 对应的状态转移方程:
    b u y 2 = max ⁡ { b u y 2 ′ , s e l l 1 ′ − p r i c e s [ i ] } buy_2=\max\{buy'_2, sell'_1-prices[i]\} buy2=max{buy2,sell1prices[i]} s e l l 2 = max ⁡ { s e l l 2 ′ , b u y 2 ′ + p r i c e s [ i ] } sell_2=\max\{sell'_2,buy'_2+prices[i]\} sell2=max{sell2,buy2+prices[i]}

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

因此,在状态转移时,我们可以直接写成: b u y 1 = max ⁡ { b u y 1 , − p r i c e s [ i ] } s e l l 1 = max ⁡ { s e l l 1 , b u y 1 + p r i c e s [ i ] } b u y 2 = max ⁡ { b u y 2 , s e l l 1 − p r i c e s [ i ] } s e l l 2 = max ⁡ { s e l l 2 , b u y 2 + p r i c e s [ i ] } \begin{aligned} buy_1 &= \max\{buy_1, -prices[i]\} \\ sell_1 &= \max\{sell_1,buy_1+prices[i]\} \\ buy_2 &= \max\{buy_2, sell_1-prices[i]\} \\ sell_2 &= \max\{sell_2,buy_2+prices[i]\} \end{aligned} buy1sell1buy2sell2=max{buy1,prices[i]}=max{sell1,buy1+prices[i]}=max{buy2,sell1prices[i]}=max{sell2,buy2+prices[i]}例如在计算 sell 1 \textit{sell}_1 sell1时,我们直接使用 buy 1 \textit{buy}_1 buy1 而不是 buy 1 ′ \textit{buy}'_1 buy1 进行转移。 buy 1 \textit{buy}_1 buy1 buy 1 ′ \textit{buy}'_1 buy1 多考虑的是在第 i i i 天买入股票的情况,而转移到 sell 1 \textit{sell}_1 sell1时,考虑的是在第 i i i 天卖出股票的情况,这样在同一天买入并且卖出收益为零,不会对答案产生影响。同理对于 buy 2 \textit{buy}_2 buy2 以及 sell 2 \textit{sell}_2 sell2,我们同样可以直接根据第 i i i 天计算出的值来进行状态转移。

那么对于边界条件,我们考虑第 i = 0 i=0 i=0 天时的四个状态:

  • buy 1 \textit{buy}_1 buy1即为以 prices [ 0 ] \textit{prices}[0] prices[0] 的价格买入股票,因此 buy 1 = − prices [ 0 ] \textit{buy}_1=-\textit{prices}[0] buy1=prices[0]
  • sell 1 \textit{sell}_1 sell1即为在同一天买入并且卖出,因此 sell 1 = 0 \textit{sell}_1=0 sell1=0
  • buy 2 \textit{buy}_2 buy2即为在同一天买入并且卖出后再以 prices [ 0 ] \textit{prices}[0] prices[0] 的价格买入股票,因此 buy 2 = − prices [ 0 ] \textit{buy}_2=-\textit{prices}[0] buy2=prices[0]
  • 同理可得 sell 2 = 0 \textit{sell}_2=0 sell2=0

我们将这四个状态作为边界条件,从 i = 1 i=1 i=1 开始进行动态规划,即可得到答案。

在动态规划结束后,由于我们可以进行不超过两笔交易,因此最终的答案在 0 0 0 sell 1 \textit{sell}_1 sell1 sell 2 \textit{sell}_2 sell2 中,且为三者中的最大值。然而我们可以发现,由于在边界条件中 sell 1 \textit{sell}_1 sell1 sell 2 \textit{sell}_2 sell2 的值已经为 0 0 0,并且在状态转移的过程中我们维护的是最大值,因此 sell 1 \textit{sell}_1 sell1 sell 2 \textit{sell}_2 sell2 最终一定大于等于 0 0 0。同时,如果最优的情况对应的是恰好一笔交易,那么它也会因为我们在转移时允许在同一天买入并且卖出这一宽松的条件,从 sell 1 \textit{sell}_1 sell1 转移至 sell 2 \textit{sell}_2 sell2,因此最终的答案即为 sell 2 \textit{sell}_2 sell2

代码

class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        int buy1 = -prices[0], sell1 = 0;
        int buy2 = -prices[0], sell2 = 0;
        for (int i = 1; i < n; ++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;
    }
}

通用思想

先做出交易 k k k 次的通解,然后令 k = 2 k=2 k=2 即可。

class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        int k = 3;								//k次交易的通解
        int[] buy = new int[k];
        Arrays.fill(buy, - prices[0]);
        int[] sell = new int[k];
        for (int i = 1; i < n; ++i) {
        	for(int j = 1; j < k; j++){
        		buy[j] = Math.max(buy[j], sell[j - 1] - prices[i]);
            	sell[j] = Math.max(sell[j], buy[j] + prices[i]);
        	}
        }
        return sell[k - 1];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值