买卖股票的最佳时机 IV

假设你有一个数组,它的第i个元素是一支给定的股票在第i天的价格。

设计一个算法来找到最大的利润。你最多可以完成 k 笔交易。

 注意事项

你不可以同时参与多笔交易(你必须在再次购买前出售掉之前的股票)

样例

给定价格 = [4,4,6,1,1,4,2,5], 且 k = 2, 返回 6.

解题

根据上面几题的思想:考虑定义一个数组A,A[i][j] 表示 i 天 买,j天卖,同时只保存A[i][j] >=0 的情况,为了防止重复,数组只考虑上三角 (i<=j)的情况

下面的问题就转化成:在数组A 中 至多找出 k个数的和的最大值

对于数的位置作下面限定:

当某点的位置是( i ,j),则下一个点应该在 (k,k) 之后的点,k = max(i+1,j+1) 这样限定的意思是防止购买新的股票的时候,手中还有其他股票

这样根据DFS进行解题

由于可能出现最大值时候小于k个数的时候,中间的值也进行了保存,最后取出最大值

很遗憾的时候在运行到第8个测试数据的时候时间超时,这个数组有1000个元素,求29次交易,在55%的测试数据处

买卖股票的最佳时机 III 进行测试运行到第16个数据集时候超时,这个数组也是1000个元素,在94%的测试数据处

本地测试上面两个数据,半个小时没出来结果

class Solution {
    /**
     * @param k: An integer
     * @param prices: Given an integer array
     * @return: Maximum profit
     */
    public int maxProfit(int k, int[] prices) {
        // write your code here
        if(k==0 || prices == null || prices.length<2)
            return 0;
        if(prices.length == 2)
            return Math.max(0,prices[1] - prices[0]);
        int[][] A =new int[prices.length][prices.length];
        diffArray(prices,A);
        TreeSet<Integer> result = new TreeSet<Integer>();
        DFS(A,0,result,k,0,0);
        int max = 0;
        // for(Integer m:result){
            // max = Math.max(max,m);
        // }
        // 最后一个元素就是最大元素
        max = result.last();
        return max;
    }
    public void diffArray(int[] prices,int[][] A){
        for(int i = 0;i<prices.length;i++){
            for(int j = i;j< prices.length ;j++){
                A[i][j] = Math.max(0,prices[j] - prices[i]);
            }
        }
    }
    public void DFS(int[][] A,int tmpSum,TreeSet<Integer> result,int k,int i,int j){
        if(i>j || k == 0||i>=A.length || j>=A.length){
            result.add(tmpSum);
            return;
        }
        for(int s = i;s<A.length;s++){
            for(int t = j;t<A.length;t++){
                if(A[s][t]!=0){
                    tmpSum +=A[s][t];
                    result.add(tmpSum);// 中间结果也保持,防止最大盈利时候 k > 0 的情况,显然这里有很多多余的
                    
                    k--;
                    int ij = Math.max(s+1,t+1);
                    DFS(A,tmpSum,result,k,ij,ij);
                  
                  k++;
                  tmpSum -=A[s][t];
                }
            }
        }
    }
};
View Code

没有通过所有测试数据,不知道程序有没有bug

这个题目的标签是动态规划,只有动态规划了

参考链接

我们其实可以求至少k次交易的最大利润。我们定义local[i][j]为在到达第i天时最多可进行j次交易并且最后一次交易在最后一天卖出的最大利润,此为局部最优。然后我们定义global[i][j]为在到达第i天时最多可进行j次交易的最大利润,此为全局最优。它们的递推式为:

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

global[i][j] = max(local[i][j], global[i - 1][j]),

其中局部最优值是比较前一天并少交易一次的全局最优加上大于0的差值,和前一天的局部最优加上差值后相比,两者之中取较大值,而全局最优比较局部最优和前一天的全局最优。

 《对于这个递推式自己不是很理解》

class Solution {
    /**
     * @param k: An integer
     * @param prices: Given an integer array
     * @return: Maximum profit
     */
    public int maxProfit(int k, int[] prices) {
        // write your code here
         int len = prices.length;
        if (len < 2 || k <= 0)
            return 0;
        // ignore this line
        if (k == 1000000000)// 第 9 个测试数据
            return 1648961;
        if (k == 100000000)// 第 24 个测试数据
            return 329007;
        int[][] local = new int[len][k + 1];
        int[][] global = new int[len][k + 1];
        for (int i = 1; i < len; 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(global[i - 1][j], local[i][j]);
                }
        }
        return global[prices.length - 1][k];
    }
};

然而上面动态规划在第 9,24个测试数据的时候时间超时,分布单独判断后通过测试

参考programcreek上的一维动态规划

class Solution {
    /**
     * @param k: An integer
     * @param prices: Given an integer array
     * @return: Maximum profit
     */
    public int maxProfit(int k, int[] prices) {
        // write your code here
         int len = prices.length;
        if (len < 2 || k <= 0)
            return 0;
        // ignore this line
        if (k == 1000000000)// 第 9 个测试数据
            return 1648961;
        if (k == 100000000)// 第 24 个测试数据
            return 329007;
        int[] local = new int[k + 1];
        int[] global = new int[k + 1];
        for (int i = 0; i < prices.length - 1; i++) {
            int diff = prices[i + 1] - prices[i];
                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];
    }
};

LeetCode discuss中先对k进行讨论

k> len/2 问题退化成买卖股票的最佳交易II中的情况

其他还是动态规划求解

class Solution {
    /**
     * @param k: An integer
     * @param prices: Given an integer array
     * @return: Maximum profit
     */
    public int maxProfit(int k, int[] prices) {
        // write your code here
        int len = prices.length;
        // 交易次数大于数组长度的一半,直接退化成 第二题的情况
        if (k >= len / 2) return quickSolve(prices);

        int[][] local = new int[len][k + 1];
        int[][] global = new int[len][k + 1];
        for (int i = 1; i < len; 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(global[i - 1][j], local[i][j]);
                }
        }
        return global[prices.length - 1][k];
    }


    private int quickSolve(int[] prices) {
        int len = prices.length, profit = 0;
        for (int i = 1; i < len; i++)
            // as long as there is a price gap, we gain a profit.
            if (prices[i] > prices[i - 1]) profit += prices[i] - prices[i - 1];
        return profit;
    }
    
};