[LeetCode-JAVA] 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).

题意:给定一个数组,表示每一天该股票的价格,给定数字k,要求最大交易次数为k,求最大的利益。

思路:根据Best Time to Buy and Sell Stock III的思路,建立动态规划数组,

local[i][j]: 表示到达第i天的时候,在第i天完成最后一次交易,总共交易了j次的最大利益。

global[i][j]:表示到达第i天的时候,总共交易了j次的最大利益。

根据定义可知下面的递推公式:

对于lcoal数组,local[i][j]在local[i-1][j]的时候已经完成了j次交易,因此从i-1天到达第i天,无论差值是多少,都要加上,同时相比较的应该是global[i-1][j-1],i-1天完成了j-1次交易,第i天如果差值大于0即赚钱,则加上,不赚钱则加0,即递推公式为: local[i][j] = Math.max(local[i-1][j] + diff, global[i-1][j-1] + (diff > 0 ? diff : 0));

对于global数组就比较简单:global[i][j] = Math.max(local[i][j], global[i-1][j]);

下面是我第一次写的代码:

public class Solution {
    public int maxProfit(int k, int[] prices) {
        if(prices == null || prices.length < 2)
            return 0;
        
        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(local[i-1][j] + diff, global[i-1][j-1] + (diff > 0 ? diff : 0));
                global[i][j] = Math.max(local[i][j], global[i-1][j]);
            }
        }
        
        return global[prices.length-1][k];
    }
}

这个时候,在k值很大的时候,如果prices数组不是很大,效果会非常的差,因此开始增加对k的判断,如果大于prices数组的长度,则题可以理解为在这些天内,不限次数的交易,最大的利益,那么只需改变为下面的代码:

public class Solution {
    public int maxProfit(int k, int[] prices) {
        if(prices == null || prices.length < 2)
            return 0;
        // 增加的判断
        if(k >= prices.length) return solveMaxProfit(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(local[i-1][j] + diff, global[i-1][j-1] + (diff > 0 ? diff : 0));
                global[i][j] = Math.max(local[i][j], global[i-1][j]);
            }
        }
        
        return global[prices.length-1][k];
    }
    // 增加的判断
    public int solveMaxProfit(int[] prices) {
        int res = 0;
        for (int i = 1; i < prices.length; ++i) {
            if (prices[i] - prices[i - 1] > 0) {
                res += prices[i] - prices[i - 1];
            }
        }
        return res;
    }
}

最后参考了下别人的优化代码,自己的代码在动态规划的时候比较死板,浪费了大量无用的空间,自己观察两个数组在比较的时候,都只需要比较自己上面的一个数值,因此可以讲二维数组简化为一维,同时,在进行计算的时候,为了不覆盖上一次的内容,如果j从1 ~ k-1 下一次的循环就会利用到上一次循环的结果 而不是二位数组中的上层内容,因此j应倒叙取值,简化后的AC代码如下:

public class Solution {
    public int maxProfit(int k, int[] prices) {
        if(prices == null || prices.length < 2)
            return 0;
        if (k >= prices.length) return solveMaxProfit(prices);
        int[] local = new int[k+1];  //在第i天卖出一次 j次的 最大收益
        int[] global = new int[k+1];  // j次的最大收益
        local[0] = 0;
        global[0] = 0;
        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] + (diff > 0 ? diff : 0), local[j] + diff);
                global[j] = Math.max(local[j], global[j]);
            }
        }
        
        return global[k];
    }
    
    public int solveMaxProfit(int[] prices) {
        int res = 0;
        for (int i = 1; i < prices.length; ++i) {
            if (prices[i] - prices[i - 1] > 0) {
                res += prices[i] - prices[i - 1];
            }
        }
        return res;
    }
}

参考链接:http://blog.csdn.net/linhuanmars/article/details/23236995

原文地址:https://www.cnblogs.com/TinyBobo/p/4585054.html