1. 程式人生 > >[LeetCode] 188. Best Time to Buy and Sell Stock IV

[LeetCode] 188. Best Time to Buy and Sell Stock IV

題:https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/

題目

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most k transactions.

Note:

You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

Example 1:

Input: [2,4,1], k = 2
Output: 2
Explanation: Buy on day 1 (price = 2) and sell on day 2 (price = 4), profit = 4-2 = 2.

Example 2:

Input: [3,2,6,5,0,3], k = 2
Output: 7
Explanation: Buy on day 2 (price = 2) and sell on day 3 (price = 6), profit = 6-2 = 4.
             Then buy on day 5 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.

題目大意

求最多買 k次的情況下,最大的股票收益。

思路

動態規劃 與 問題轉化。

2個狀態:

int[][] local:local[i][j]表示到第i天時至少完成j次交易並且最後一次交易發生在第i天時的最大收益。
int[][] global:global[i][j]表示到第i天時至少完成j次交易的最大收益,最後一次交易不一定發生在第i天。

狀態轉移方程:
diff = nums[i] - nums[i-1]

local[i][j] = max(global[i-2][j-1] + diff if i>=2 else 0, global[i-1][j-1],local[i-1][j] + diff)

即,第i天剛發生第j次交易的最大收益 是

  1. 第i-2天已經發生第j-1次交易的最大收益 + 第i-1天買且第i天賣的收益。
  2. 第i-1天已經發生 至少 第j-1次交易的最大收益(相當於 第j次交易什什麼都不做)
  3. 第i-1天剛j次交易,然後加上今天的差值(這裡因為local[i-1][j]比如包含第i-1天賣出的交易,所以現在變成第i天賣出,並不會增加交易次數,而且這裡無論diff是不是大於0都一定要加上,因為否則就不滿足local[i][j]必須在最後一天賣出的條件了)

取以上 3 種的最大值。

其中 1,2 可以合併為
global[i-1][j-1]+max(diff,0)
故:
local[i][j] = max(global[i-1][j-1]+max(diff,0), local[i-1][j]+diff)

global[i][j] = max(global[i-1][j],local[i][j])
即,在第i天已經發生j次交易的最大收益 是 在第i-1天已經發生j次交易的最大收益 和 在第i天剛發生第j次交易的最大收益 中 較大值。

初始化狀態:均為 0;

參考:https://blog.csdn.net/smile_watermelon/article/details/47445981

當 k >>2 >= nums.length 時候,可以看做任務為 任意次交易。因為 每次交易至少需要 2天。 最大允許的交易次數 等於 在nums.length天最多能 完成 的交易次數,所以可看作 可以無限交易。
這樣 效率更高,因為 k 可能很大 ,為了迭代k次,會佔用 大量 記憶體與計算力 導致 不過了。

local[i][j] 未合併

class Solution {
    public int maxProfit(int k, int[] prices) {
        if(k<<1>=prices.length){
            return infiniteMaxProfit(prices);
        }
        int[][] local = new int[prices.length ][k+1];
        int[][] global = new int[prices.length ][k+1];

        for(int i = 1 ; i < prices.length ; i++){
            int diff =prices[i] - prices[i-1];
            for (int j = 1; j <= k ; j++){
                local[i][j] = i<2?0:global[i-2][j-1];
                local[i][j] = Math.max(local[i][j] + diff,global[i-1][j-1]);
                local[i][j] = Math.max(local[i][j],local[i-1][j] + diff);
                global[i][j] = Math.max(local[i][j],global[i-1][j]);
            }
        }
        return global[prices.length-1][k];

    }

    private int infiniteMaxProfit(int[] prices) {
        int []buy = new int[prices.length+1];
        int []sell = new int[prices.length+1];
        buy[0] = Integer.MIN_VALUE;
        for(int i = 1; i <= prices.length ;i++){
            buy[i] = Math.max(buy[i-1],sell[i-1]-prices[i-1]);
            sell[i] = Math.max(sell[i-1],buy[i-1]+prices[i-1]);
        }
        return sell[prices.length];
    }
}

local[i][j] 合併

class Solution {
    public int maxProfit(int k, int[] prices) {
        if(k<<1>=prices.length){
            return infiniteMaxProfit(prices);
        }
        int[][] local = new int[prices.length ][k+1];
        int[][] global = new int[prices.length ][k+1];

        for(int i = 1 ; i < prices.length ; i++){
            int diff =prices[i] - prices[i-1];
            for (int j = 1; j <= k ; j++){
                local[i][j] = Math.max(global[i-1][j-1] + Math.max(diff,0),local[i-1][j] + diff);
                global[i][j] = Math.max(local[i][j],global[i-1][j]);
            }
        }
        return global[prices.length-1][k];

    }

    private int infiniteMaxProfit(int[] prices) {
        int []buy = new int[prices.length+1];
        int []sell = new int[prices.length+1];
        buy[0] = Integer.MIN_VALUE;
        for(int i = 1; i <= prices.length ;i++){
            buy[i] = Math.max(buy[i-1],sell[i-1]-prices[i-1]);
            sell[i] = Math.max(sell[i-1],buy[i-1]+prices[i-1]);
        }
        return sell[prices.length];
    }
}

由於local[i][j] 、 global[i][j] 只是 涉及 [i-1][j] 與 [i-1][j-1] ,所以 可以優化, 但注意到 j-1 ,對 j 迭代 需要 從後往前。

class Solution {
    public int maxProfit(int k, int[] prices) {
        if(k<<1>=prices.length){
            return infiniteMaxProfit(prices);
        }
        
        int[] local = new int[k+1];
        int[] global = new int[k+1];

        for(int i = 1 ; i < prices.length ; i++){
            int diff =prices[i] - prices[i-1];
            for (int j = k; j >= 1 ; j--){
                local[j] = Math.max(global[j-1] + Math.max(diff,0),local[j] + diff);
                global[j] = Math.max(local[j],global[j]);
            }
        }
        return global[k];
    }

    private int infiniteMaxProfit(int[] prices) {
        int []buy = new int[prices.length+1];
        int []sell = new int[prices.length+1];
        buy[0] = Integer.MIN_VALUE;
        for(int i = 1; i <= prices.length ;i++){
            buy[i] = Math.max(buy[i-1],sell[i-1]-prices[i-1]);
            sell[i] = Math.max(sell[i-1],buy[i-1]+prices[i-1]);
        }
        return sell[prices.length];
    }
}