package class15; //leetcode 309 public class Code05_BestTimeToBuyAndSellStockWithCooldown { // 该方法是对的,提交之后大量的测试用例通过,最后几个会超时 // 如果把这个方法改成动态规划,是可以通过的,但这个尝试不是最优解 public static int maxProfit1(int[] prices) { return process1(prices, false, 0, 0); } // buy == false 目前可以交易,而且当前没有购买行为 // buy == true 已经买了,买入价buyPrices,待卖出 public static int process1(int[] prices, boolean buy, int index, int buyPrices) { if (index >= prices.length) { return 0; } if (buy) { int noSell = process1(prices, true, index + 1, buyPrices); int yesSell = prices[index] - buyPrices + process1(prices, false, index + 2, 0); return Math.max(noSell, yesSell); } else { int noBuy = process1(prices, false, index + 1, 0); int yesBuy = process1(prices, true, index + 1, prices[index]); return Math.max(noBuy, yesBuy); } } // 最优尝试如下: // buy[i] : 在0...i范围上,最后一次操作是buy动作, // 这最后一次操作有可能发生在i位置,也可能发生在i之前 // buy[i]值的含义是:max{ 所有可能性[之前交易获得的最大收益 - 最后buy动作的收购价格] } // 比如:arr[0...i]假设为[1,3,4,6,2,7,1...i之后的数字不用管] // 什么叫,所有可能性[之前交易获得的最大收益 - 最后buy动作的收购价格]? // 比如其中一种可能性: // 假设最后一次buy动作发生在2这个数的时候,那么之前的交易只能在[1,3,4]上结束,因为6要cooldown的, // 此时最大收益是多少呢?是4-1==3。那么,之前交易获得的最大收益 - 最后buy动作的收购价格 = 3 - 2 = 1 // 另一种可能性: // 再比如最后一次buy动作发生在最后的1这个数的时候,那么之前的交易只能在[1,3,4,6,2]上发生,因为7要cooldown的, // 此时最大收益是多少呢?是6-1==5。那么,之前交易获得的最大收益 - 最后buy动作的收购价格 = 5 - 1 = 4 // 除了上面两种可能性之外,还有很多可能性,你可以假设每个数字都是最后buy动作的时候, // 所有可能性中,(之前交易获得的最大收益 - 最后buy动作的收购价格)的最大值,就是buy[i]的含义 // 为啥buy[i]要算之前的最大收益 - 最后一次收购价格?尤其是最后为什么要减这么一下? // 因为这样一来,当你之后以X价格做成一笔交易的时候,当前最好的总收益直接就是 X + buy[i]了 // // sell[i] :0...i范围上,最后一次操作是sell动作,这最后一次操作有可能发生在i位置,也可能发生在之前 // sell[i]值的含义:0...i范围上,最后一次动作是sell的情况下,最好的收益 // // 于是通过分析,能得到以下的转移方程: // buy[i] = Math.max(buy[i - 1], sell[i - 2] - prices[i]) // 如果i位置没有发生buy行为,说明有没有i位置都一样,那么buy[i] = buy[i-1],这显而易见 // 如果i位置发生了buy行为, 那么buy[i] = sell[i - 2] - prices[i], // 因为你想在i位置买的话,你必须保证之前交易行为发生在0...i-2上, // 因为如果i-1位置有可能参与交易的话,i位置就要cooldown了, // 而且之前交易行为必须以sell结束,你才能buy,而且要保证之前交易尽可能得到最好的利润, // 这正好是sell[i - 2]所代表的含义,并且根据buy[i]的定义,最后一定要 - prices[i] // // sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i]) // 如果i位置没有发生sell行为,那么sell[i] = sell[i-1],这显而易见 // 如果i位置发生了sell行为,那么我们一定要找到 {之前获得尽可能好的收益 - 最后一次的收购价格尽可能低}, // 而这正好是buy[i - 1]的含义!之前所有的"尽可能"中,最好的一个! public static int maxProfit2(int[] prices) { if (prices.length < 2) { return 0; } int N = prices.length; int[] buy = new int[N]; int[] sell = new int[N]; // buy[0] 不需要设置 buy[0] = -arr[0] // sell[0] = 0 buy[1] = Math.max(-prices[0], -prices[1]); sell[1] = Math.max(0, prices[1] - prices[0]); for (int i = 2; i < N; i++) { buy[i] = Math.max(buy[i - 1], sell[i - 2] - prices[i]); sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i]); } return sell[N - 1]; } // 最优解就是方法2的空间优化而已 public static int maxProfit3(int[] prices) { if (prices.length < 2) { return 0; } int buy1 = Math.max(-prices[0], -prices[1]); int sell1 = Math.max(0, prices[1] - prices[0]); int sell2 = 0; for (int i = 2; i < prices.length; i++) { int tmp = sell1; sell1 = Math.max(sell1, buy1 + prices[i]); buy1 = Math.max(buy1, sell2 - prices[i]); sell2 = tmp; } return sell1; } }