买卖股票的最佳时机 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]; }

浙公网安备 33010602011771号