买卖股票的最佳时机 III

题目

 

解析

/**
 * 买卖股票的最佳时机 III
 * 设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易
 * 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)
 * 也就是说两次购买是存在间隔差的
 *
 * 思路:
 * 进行k次交易的算法,然后最多进行两次我们只需要把k取成2即可。
 * 使用“局部最优和全局最优解法”。
 * 维护两种量:
 * 一个是当前到达第i天可以最多进行j次交易,最好的利润是多少(global[i][j]),
 * 另一个是当前到达第i天,最多可进行j次交易,并且最后一次交易在当天卖出的最好的利润是多少(local[i][j])
 * @param prices
 * @return
 */
public static int maxProfit(int[] prices) {
//        System.out.println(Arrays.toString(prices));
    if (prices == null || prices.length < 2){
        return 0;
    }
    int k = 2;
    int size = prices.length;
    int[][] local = new int[size][k+1];
    int[][] global = new int[size][k+1];

    for (int i= 1; i < size; 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]);
        }
    }
//        System.out.println(Arrays.deepToString(local));
//        System.out.println(Arrays.deepToString(global));
    return  global[size-1][k];
}

变量解析:

1.首先理解局部变量的形成:
local[i][j] = Math.max(global[i - 1][j - 1] + Math.max(diff, 0), local[i - 1][j] + diff);
1)首先针对第一次买卖 (直接转):
Math.max(global[i - 1][0] + Math.max(diff, 0), local[i - 1][1] + diff);
可以看作为
Math.max(0 + Math.max(diff, 0), local[i - 1][1] + diff);
其中 Math.max(diff, 0) 可以看作是昨天买今天买
而 local[i - 1][1] + diff 则可以看作是能否连起来

2)接着针对第二次买卖 (直接转)[难理解的重点]:
Math.max(global[i - 1][1] + Math.max(diff, 0), local[i - 1][2] + diff);
其中 global[i - 1][1] + Math.max(diff, 0)
取第一次买卖的全局最优解,加上今天的差价作为第二次买卖的结果[昨天买今天卖]

而 local[i - 1][2] + diff
能否将第二次买卖连续起来,如果diff不大于0是连不起来,那时候可以不考虑


2.首先理解全局变量的形成:
global[i][j] = Math.max(local[i][j], global[i - 1][j]);
1)首先针对第一次买卖 (直接转):
Math.max(local[i][1], global[i - 1][1]);
如果当天是最优解的话就保存,如果当天不是就保存之前的最优解
2)接着针对第二次买卖 (直接转):
Math.max(local[i][2], global[i - 1][2]);
如果当天是最优解的话就保存,如果当天不是就保存之前的最优解

进一步优化:

思路:由于在这个过程我们会发现,增加的次数都是追寻上一次的结果来进行计算的,所以参数保存的其余数据都不重要,这也就表示了可以不记录那些数据

 public static int maxProfit(int[] prices) {
//        System.out.println(Arrays.toString(prices));
        if (prices == null || prices.length < 2){
            return 0;
        }
        int k = 2;
        int size = prices.length;
        int[] local = new int[k+1];
        int[] global = new int[k+1];

        for (int i= 1; i < size; i++){
            // 当天差价
            int diff = prices[i] - prices[i - 1];
            //正序的情况
//            for(int j = 1; j <= k ; j++){
//                local[j] = Math.max(global[j - 1] + Math.max(diff, 0), local[j] + diff);
//                global[j] = Math.max(local[j], global[j]);
//            }
            // 存在的问题,这里是需要倒序的,为什么不能是正序
            // 因为当正序的时候第一次,global[j - 1] + Math.max(diff, 0)是没有问题的
            // 但计算第二次买卖的时候,global[j - 1]由于在第一次的时候被赋予值了,所以计算第二次的时候会变成翻倍的情况
            // 所以正序会出现问题
            for(int j = 2; j > 0 ; j--){
                local[j] = Math.max(global[j - 1] + Math.max(diff, 0), local[j] + diff);
                global[j] = Math.max(local[j], global[j]);
            }
            System.out.println("");
            System.out.println(Arrays.toString(local));
            System.out.println(Arrays.toString(global));
        }

        return  global[k];
    }

 

最优的方式:

public static int maxProfit2(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,是手头买入的时候还剩余的钱
            buy2 = Math.max(buy2, sell1 - prices[i]);
            //第二次卖出的话,就是买入剩余的钱加上当时的股价对比如果之前卖出的钱取最大值
            sell2 = Math.max(sell2, buy2 + prices[i]);
        }
        return sell2;
    }

这种都是由原版动态规划简化过来的,原版:

public int maxProfit3(int[] prices) {
        int n =prices.length;
        int[][][] dp = new int[n][2][2];
        dp[0][0][0] = -prices[0];
        dp[0][0][1] = -prices[0];
        for(int i = 1;i<n;i++){
            dp[i][0][0] = Math.max(dp[i-1][0][0],-prices[i]);
            dp[i][1][0] = Math.max(dp[i-1][1][0],dp[i][0][0]+prices[i]);
            dp[i][0][1] = Math.max(dp[i-1][0][1],dp[i][1][0]-prices[i]);
            dp[i][1][1] = Math.max(dp[i-1][1][1],dp[i][0][1]+prices[i]);
        }
        return dp[n-1][1][1];
    }

 

关联

买卖股票的最佳时机 IV

posted @ 2025-04-15 18:42  忧愁的chafry  阅读(15)  评论(0)    收藏  举报