You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

102 lines
4.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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;
}
}