LeetCode123——买卖股票的最佳时机III

我的LeetCode代码仓:https://github.com/617076674/LeetCode

原题链接:https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/description/

题目描述:

LeetCode123——买卖股票的最佳时机III_第1张图片

知识点:动态规划

思路一:分解成两个LeetCode121——买卖股票的最佳时机子问题

枚举第一笔交易卖出的时间firstDealSell,对[0, firstDealSell]范围内的价格求解LeetCode121——买卖股票的最佳时机中的问题,得到结果result1,再对[firstDealSell, n - 1]范围内的价格再一次求解LeetCode121——买卖股票的最佳时机中的问题,其中n为prices数组的大小,得到结果result2,求result1 + result2的最大值。

时间复杂度是O(n ^ 2)。空间复杂度是O(1)。

当然,在此基础之上,结合LeetCode122——买卖股票的最佳时机II,我们可以做一些小小的优化。

对于第一次和第二次卖出的时间点,firstDealSell和secondDealSell,其之前的价格一定是一个上坡,其之后的价格一定是一个下坡,我们在价格坡顶卖出。

JAVA代码:

public class Solution {
    public int maxProfit(int[] prices) {
        int result = 0;
        if(prices.length == 0){
            return result;
        }
        int firstDealSell;  //第一笔交易在firstDealSell卖出
        int secondDealSell; //第二笔交易在secondDealSell卖出
        for(secondDealSell = prices.length - 1; secondDealSell > 0; secondDealSell--){
            if(prices[secondDealSell - 1] < prices[secondDealSell]){
                break;
            }
        }
        for(firstDealSell = 1; firstDealSell < prices.length; firstDealSell++){
            while(firstDealSell + 1 < prices.length && prices[firstDealSell + 1] >= prices[firstDealSell]){
                firstDealSell++;
            }
            int result1 = maxProfit(prices, 0, firstDealSell);
            int result2 = maxProfit(prices, firstDealSell + 1, secondDealSell);
            if(result1 + result2 > result){
                result = result1 + result2;
            }
        }
        return result;
    }

    private int maxProfit(int[] prices, int left, int right){
        int result = 0;
        if(right - left < 1){
            return result;
        }
        int minPrice = prices[left];
        for(int i = left + 1; i <= right; i++){
            result = Math.max(result, prices[i] - minPrice);
            minPrice = Math.min(minPrice, prices[i]);
        }
        return result;
    }
}

LeetCode解题报告:

LeetCode123——买卖股票的最佳时机III_第2张图片

思路二:动态规划

本思路及下述改进思路均参考自:https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/discuss/135704/Detail-explanation-of-DP-solution。

状态定义:f(x, y) -------- 第x笔交易在第y天能取得的最大利润

状态转移

(1)当x == 1时

        a:我们可以选择在第y天不卖出也不买入,

                a-1:如果此时y == 0,即第0天,那么f(1, 0) = 0,即取得的最大利润为0。

                a-2:如果此时y > 0,那么此时我们的第x笔交易在第y天能取得的最大利润是f(1, y - 1),因为我们在第y天既不买入也不卖出,取得的最大利润自然和第x笔交易在第y - 1天能取得的最大利润相同。

        b:我们可以选择在第y天卖出,

                b-1:如果此时y == 0,显然,我们不可能在第0天卖出,这种情况不予讨论。

                b-2:如果此时y > 0,f(x, y) = max(prices[y] - prices[b]),其中0 <= b < y,代表我们在第b天买入。

综上,对于x == 1的情况,当y == 0时,f(1, 0) = 0;当y > 0时,f(1, y) = max(f(1, y - 1), prices[y] - prices[b]),0 <= b < y

(2)当x == 2时

        a:我们可以选择在第y天不卖出也不买入,

                a-1:如果此时y == 0,即第0天,那么f(x, y) = 0,即取得的最大利润为0。

                a-2:如果此时y > 0,那么此时我们的第x笔交易在第y天能取得的最大利润是f(2, y - 1),因为我们在第y天既不买入也不卖出,取得的最大利润自然和第x笔交易在第y - 1天能取得的最大利润相同。

        b:我们可以选择在第y天卖出,

                b-1:如果此时y == 0,显然,我们不可能在第0天卖出,这种情况不予讨论。

                b-2:如果此时y > 0,f(x, y) = max(prices[y] - prices[b] + f(1, b - 1)),其中0 <= b < y,代表我们在第b天买入。

综上,对于x == 2的情况,当y == 0时,f(2, 0) = 0;当y > 0时,f(2, y) = max(f(2, y - 1), prices[y] - prices[b] + f(1, b - 1)),0 <= b < y

时间复杂度是O(kn ^ 2),其中k为交易次数,n为prices数组的大小。空间复杂度是O(kn)。

JAVA代码:

public class Solution {
    public int maxProfit(int[] prices) {
        int result = 0;
        if(0 == prices.length){
            return result;
        }
        int[][] dp = new int[2][prices.length];
        for(int k = 0; k < 2; k++){
            dp[k][0] = 0;
            int min = prices[0];
            for(int i = 1; i < prices.length; i++){
                for(int b = 1; b < i; b++){
                    if(k == 0){
                        min = Math.min(min, prices[b]);
                    }else{
                        min = Math.min(min, prices[b] - dp[k - 1][b - 1]);
                    }
                }
                dp[k][i] = Math.max(dp[k][i - 1], prices[i] - min);
            }
        }
        return dp[1][prices.length - 1];
    }
}

LeetCode解题报告:

LeetCode123——买卖股票的最佳时机III_第3张图片

思路三:对思路二的改进

在思路二中,我们对第一笔交易和第二笔交易进行了分别计算,因为第1笔交易的前一笔交易不存在,会产生数组越界问题。

我们可以在第一笔交易前增加一笔虚拟的第0笔交易,其f(0, y)均为0,这样就避免了对第一笔交易和第二笔交易的讨论。

同时,我们会发现思路二中min的计算其实重复进行了,我们完全可以忽略内层的循环变量b的循环。

时间复杂度和空间复杂度均是O(kn),其中k为交易次数,n为prices数组的大小。

JAVA代码:

public class Solution {
    public int maxProfit(int[] prices) {
        int result = 0;
        if (0 == prices.length) {
            return result;
        }
        int[][] dp = new int[3][prices.length];
        for(int k = 1; k <= 2; k++){
            dp[k][0] = 0;
            int min = prices[0];
            for(int i = 1; i < prices.length; i++){
                dp[k][i] = Math.max(dp[k][i - 1], prices[i] - min);
                min = Math.min(min, prices[i] - dp[k - 1][i - 1]);
            }
        }
        return dp[2][prices.length - 1];
    }
}

LeetCode解题报告:

LeetCode123——买卖股票的最佳时机III_第4张图片

思路四:思路三的另一种形式

交换内外层循环的位置。

时间复杂度和空间复杂度均是O(kn),其中k为交易次数,n为prices数组的大小。

JAVA代码:

public class Solution {
    public int maxProfit(int[] prices) {
        int result = 0;
        if (0 == prices.length) {
            return result;
        }
        int[][] dp = new int[3][prices.length];
        int[] min = new int[3];
        for(int i = 1; i < 3; i++){
            min[i] = prices[0];
        }
        for(int i = 1; i < prices.length; i++){
            for(int k = 1; k <= 2; k++){
                dp[k][i] = Math.max(dp[k][i - 1], prices[i] - min[k]);
                min[k] = Math.min(min[k], prices[i] - dp[k - 1][i - 1]);
            }
        }
        return dp[2][prices.length - 1];
    }
}

LeetCode解题报告:

LeetCode123——买卖股票的最佳时机III_第5张图片

思路五:思路四的改进

从思路四中我们发现第i列的变量只依赖于第i - 1列的变量,压缩该维度。

时间复杂度是O(kn),其中k为交易次数,n为prices数组的大小。空间复杂度是O(k)。

JAVA代码:

public class Solution {
    public int maxProfit(int[] prices) {
        int result = 0;
        if (0 == prices.length) {
            return result;
        }
        int[] dp = new int[3];
        int[] min = new int[3];
        for(int i = 1; i < 3; i++){
            min[i] = prices[0];
        }
        for(int i = 1; i < prices.length; i++){
            for(int k = 1; k <= 2; k++){
                dp[k] = Math.max(dp[k], prices[i] - min[k]);
                min[k] = Math.min(min[k], prices[i] - dp[k - 1]);
            }
        }
        return dp[2];
    }
}

LeetCode解题报告:

LeetCode123——买卖股票的最佳时机III_第6张图片

思路六:思路五的改进

针对本题,只进行2次交易,优化代码。

时间复杂度是O(n),n为prices数组的大小。空间复杂度是O(1)。

JAVA代码:

public class Solution {
    public int maxProfit(int[] prices) {
        int buy1 = Integer.MAX_VALUE;
        int sell1 = 0;
        int buy2 = Integer.MAX_VALUE;
        int sell2 = 0;
        for(int i = 0; i < prices.length; i++){
            sell1 = Math.max(sell1, prices[i] - buy1);
            buy1 = Math.min(buy1, prices[i]);
            sell2 = Math.max(sell2, prices[i] - buy2);
            buy2 = Math.min(buy2, prices[i] - sell1);
        }
        return sell2;
    }
}

LeetCode解题报告:

LeetCode123——买卖股票的最佳时机III_第7张图片

 

你可能感兴趣的:(LeetCode题解)