Java描述 LeetCode,123. Best Time to Buy and Sell Stock III(买卖股票的时机III)

大家好,我是河海哥,专注于后端,如果可以的话,想做一名code designer而不是普通的coder,一起见证河海哥的成长,您的评论与赞是我的最大动力。

1-1:题目

You are given an array prices where prices[i] is the price of a given stock on the ith day.

Find the maximum profit you can achieve. You may complete at most two transactions.

Note: You may not engage in multiple transactions simultaneously (i.e., you must sell the stock before you buy again).

Example 1:

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

Example 2:

Input: prices = [1,2,3,4,5]
Output: 4
Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4.
Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are engaging multiple transactions at the same time. You must sell before buying again.

Example 3:

Input: prices = [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.

Example 4:

Input: prices = [1]
Output: 0

Constraints:

1 <= prices.length <= 105
0 <= prices[i] <= 105

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii

1-2:idea

  • 思路1:有问题2的答案,把持有股票作为一种状态的思想,我想着能不能再加一个维度去代表是第几次交易,这样就可以保证,第二次交易一定在第一次交易之后。然后我就按照这个思路写了一份代码,事实证明,还是比较复杂的,尤其在初始化的时候,没有想好,一只不能ac。
  • 思路2:对于某一天的交易情况一共有5种
    - 没有操作,不买入也不卖出
    - 第一次买入
    - 第一次卖出
    - 第二次买入
    - 第二次卖出
    
    那么就可以用dp[i][j] 代表第i天,处于第j个状态时的收入。这边确实要注意,比如处于第二次买入的状态,并不一定说那一天必须是买入,也可能是之前发生过第二次买入,但是当天没有买入。比如[买,无,卖,无,无,无,买,无,无,i]那么i也是处于第二次买入的状态,因为之前已经出现过两次买入了,没有处于二次买的状态,那么i+1天就不能卖了。

1-3:idea1代码

public static int maxProfit(int[] prices) {
    int n = prices.length;
    // dp[i][j][k] 代表第i[1,n]天,是否持有股票[0,1],第k[0,2]次交易的最大收入,规定卖出的时候交易次数+1
    int[][][] dp = new int[n + 1][2][3];

    for (int k = 0; k <= 2; k++) {
        dp[1][0][k] = 0;
        dp[1][1][k] = -prices[0];
    }

    for (int i = 2; i < n + 1; i++) {
        int price = prices[i - 1];
        for (int j = 0; j <= 2; j++) {
            // 第0次交易
            if (j == 0) {
                dp[i][0][j] = dp[i - 1][0][j];
            } else {
                // 今天不持有 = max(昨天不持有,昨天持有状态+今天卖出),昨天持有状态一定是上一次交易,所以这边j是要-1的
                dp[i][0][j] = Math.max(dp[i - 1][0][j], dp[i - 1][1][j - 1] + price);
            }
            // 今天持有 = max(昨天持有,昨天不持有状态+今天买入),昨天不持有状态,今天持有,仍然是一个交易之下,j不用动。
            dp[i][1][j] = Math.max(dp[i - 1][1][j], dp[i - 1][0][j] - price);
        }
    }
    List<Integer> list = new ArrayList<>();
    System.out.println(Arrays.deepToString(dp));
    for (int i = 0; i <= 1; i++) {
        for (int j = 0; j <= 2; j++) {
            // dp[n][0][0], dp[n][1][0], dp[n][0][1], dp[n][1][1], dp[n][0][2], dp[n][1][2]
            list.add(dp[n][i][j]);
        }
    }
    return Collections.max(list);
}

有几个注意点:

  • 这段代码是简化之后的,你可以先把j=0,1,2的时候dp状态转移方程依次列出来,之后再这样进行简化。我一开始是不能直接这样用j内循环写出来的。
  • 最大值的位置:最后找最大值其实也有技巧,但是我没有想到,最后一次卖出的时候是最大值的位置。
  • 初始化:初始化这边也有点困难,我一开始感觉当天买,当天卖不符合题意,题目说当天买不能当天卖,应该不存在这种情况,但是看完解答,当天买当天卖不会影响最后的结果,比如dp[1][0][2] = 0; 第一天不持有股票,并且是第二次卖出的情况,也就是买,卖,买,卖这几个都发生了在同一天。

1-4:idea2代码

public static int maxProfit2(int[] prices) {
    int n = prices.length;
    // dp[i][j]代表第i天,
    // j代表第i天第状态,共五种。
    // - 0:当天无交易
    // - 1:第一次买入
    // - 2:第一次卖出
    // - 3:第二次买入
    // - 4:第二次卖出
    int[][] dp = new int[n + 1][5];

    // initialization
    for (int i = 0; i < dp[0].length; i++) {
        if (i % 2 == 1) {
            dp[1][i] = -prices[0];
        }
    }
    for (int i = 2; i < n + 1; i++) {
        int price = prices[i - 1];
        dp[i][0] = dp[i - 1][0];
        for (int j = 1; j <= 4; j++) {
            // j is odd
            if (j % 2 == 1) {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] - price);
            } else {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] + price);
            }
        }
//            // 第i天第一次买入 = max(当天无操作,当天买入),dp[i - 1][0]代表前一天处于无操作下的最大收入。不处于无操作状态,就不能第一次买入。
//            dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - price);
//            // 第i天第一次卖出 = max(当天无操作,当天卖出),dp[i - 1][1]代表前一天处于买入状态下的最大收入。不处于买入状态,就不能第一次买入。
//            dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + price);
//            dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - price);
//            dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + price);
    }
    System.out.println(Arrays.deepToString(dp));
    return dp[n][4];
}

这里注释足够细致,我把所有的dp状态转移方程都写出来了。应该比较好理解了。初始化,也是之前的那个思路,我们可以用滚动数组的思想,给出第二版:

public static int maxProfit3(int[] prices) {
    int n = prices.length;
    // dp[i][j]代表第i天,
    // j代表第i天第状态,共五种。
    // - 0:当天无交易
    // - 1:第一次买入
    // - 2:第一次卖出
    // - 3:第二次买入
    // - 4:第二次卖出
    int[][] dp = new int[n + 1][5];
    int nothing = 0;
    int buy1 = -prices[0];
    int sell1 = 0;
    int buy2 = -prices[0];
    int sell2 = 0;

    for (int i = 2; i < n + 1; i++) {
        int price = prices[i - 1];
        buy1 = Math.max(buy1, nothing - price);
        sell1 = Math.max(sell1, buy1 + price);
        buy2 = Math.max(buy2, sell1 - price);
        sell2 = Math.max(sell2, buy2 + price);
    }

    return sell2;
}

1-5:总结

这道题有点难,我自己的想法靠自己写无法ac,根据别人的评论,自己修改才最后ac,答案的那种方法,我确实想不到,用一个维度去列举每一天的所有状态。就当是一种解题的积累吧!加油呀,河海哥,至少保证以后做过的题会写就行,没做过的不会也没办法。您的评论和点赞是我续写的动力。如有错误,请指正,个人水平很有限。

上一篇:Jsonpath 常用解析规则总结


下一篇:Pandas高级操作