Idiot-maker

  :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

https://oj.leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most two transactions.

Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

解题思路:

首先想到,按照Best Time to Buy and Sell Stock问题的解法,找到一个最大的区间,然后再找第二大的区间,这个第二大区间必然不能和第一大区间重合,于是便多了一个变量buyDay来记录,除此之外还多了好几个变量。但是,这个方法是错误的。为什么?因为对于可以买卖两次的问题,Best Time to Buy and Sell Stock问题的最大区间,在这里,有时候还不如分开来核算。想想,例如,6,1,3,2,4,7.问题一的最大区间为1-7=6.如果分开两次,1-3+2-7=7.所以以下代码是错误的,而且也不太容易理解。

public class Solution {
    public int maxProfit(int[] prices) {
        int maxProfit = 0;
        int secondMaxProfit = 0;
        int profit = 0;
        int thisTimeMaxProfit = 0;
        int buyDay = 0;
        int maxBuyDay = 0;
        int buyValue;
        
        if(prices.length == 0){
            return 0;
        }else {
            buyValue = prices[0];
            buyDay = 0;
        }
        
        for(int i = 0; i < prices.length; i ++){
            profit = prices[i] - buyValue;
            
            if(profit > maxProfit){
                maxProfit = profit;
                maxBuyDay = buyDay;
            }
            //更新一个同一个买天的最大值,即local,不是global的最大利润
            if(profit > thisTimeMaxProfit){
                thisTimeMaxProfit = profit;
            }
            
            //出现新的最低买价,既要看看,这个local区间内的最大利润,即thisTimeMaxProfit,是不是可以作为第二大利润
            //所以还要判断买天是不是和最大利润为一天,防止重复
            if(prices[i] < buyValue){
                if(thisTimeMaxProfit > secondMaxProfit && thisTimeMaxProfit <= maxProfit && buyDay != maxBuyDay){
                    secondMaxProfit = thisTimeMaxProfit;
                }
                buyValue = prices[i];
                buyDay = i;
                profit = 0;
                thisTimeMaxProfit = 0;
            }
            //在最后一天,如果价格一直上扬,碰不到一个新买点,也要更新第二大利润
            if(i == prices.length - 1 && buyDay != maxBuyDay){
                if(thisTimeMaxProfit > secondMaxProfit && thisTimeMaxProfit <= maxProfit){
                    secondMaxProfit = thisTimeMaxProfit;
                }
            }
        }
        return maxProfit + secondMaxProfit;
    }
}

于是出现了下面的解法。因为两次交易必然不会重合的,肯定被第k天分割在左右。所以遍历整个区间,以每天作为分割,求出左右两个区间的最大利润,再将这一对对利润相加,求出总利润中最大的。但是,这个代码的时间复杂度要O(n^2),超时。

public class Solution {
    public int maxProfit(int[] prices) {
        int maxProfit = 0;
        int profit = 0;
        int buyValue;
        int leftMaxProfit = 0;
        int rightMaxProfit = 0;
        
        if(prices.length == 0){
            return 0;
        }else {
            buyValue = prices[0];
        }
        
        for(int i = 0; i < prices.length; i ++){
            profit = prices[i] - buyValue;
            
            if(profit > maxProfit){
                maxProfit = profit;
            }
            
            if(prices[i] < buyValue){
                buyValue = prices[i];
                profit = 0;
            }
        }
        
        for(int i = 0; i < prices.length; i ++){
            buyValue = prices[0];
            for(int j = 0; j < i; j++){
                profit = prices[j] - buyValue;
            
                if(profit > leftMaxProfit){
                  leftMaxProfit = profit;
                }
            
                if(prices[i] < buyValue){
                    buyValue = prices[j];
                    profit = 0;
                }
            }
            buyValue = prices[i];
            for(int k = i; k < prices.length; k++){
                profit = prices[k] - buyValue;
            
                if(profit > rightMaxProfit){
                  rightMaxProfit = profit;
                }
            
                if(prices[i] < buyValue){
                    buyValue = prices[k];
                    profit = 0;
                }
            }
            if(leftMaxProfit + rightMaxProfit > maxProfit){
                maxProfit = leftMaxProfit + rightMaxProfit;
            }
        }
        return maxProfit;
    }
}

我们只好空间换时间,分开执行两次遍历,但用两个数组,分别记录从左和从右开始的最大利润,最后将两者相加,再次遍历求出总利润最大的。

public class Solution {
    public int maxProfit(int[] prices) {
        int profit = 0;
        int buyValue;
        int sellValue;
        int[] leftMaxProfit = new int[prices.length];
        int[] rightMaxProfit = new int[prices.length];
        int[] maxProfit = new int[prices.length];
        int maxProfitAll = 0;
        
        if(prices.length == 0){
            return 0;
        }else {
            buyValue = prices[0];
        }
        
        for(int i = 1; i < prices.length; i++){
            //这里必须注意dp中状态转化方程,不可写为leftMaxProfit[i] = prices[i] - buyValue
            //考虑一下,已经过了最大利润区间,利润开始下降,但是该区间内的local最大利润仍然是前面的那个最大值
            //所以要在leftMaxProfit[i - 1]和当前值取最大
            leftMaxProfit[i] = Math.max(leftMaxProfit[i - 1], prices[i] - buyValue);
            
            if(prices[i] < buyValue){
                buyValue = prices[i];
            }
        }
        
        if(prices.length == 0){
            return 0;
        }else {
            sellValue = prices[prices.length - 1];
        }
        
        for(int i = prices.length - 2; i >= 0; i--){
            rightMaxProfit[i] = Math.max(rightMaxProfit[i + 1], sellValue - prices[i]);
            
            if(prices[i] > sellValue){
                sellValue = prices[i];
            }
        }
        
        for(int i = 0; i < prices.length; i++){
            maxProfit[i] = leftMaxProfit[i] + rightMaxProfit[i];
        }
        for(int i = 0; i < prices.length; i++){
            maxProfitAll = Math.max(maxProfitAll, maxProfit[i]);
        }
        
        return maxProfitAll;
    }
}

总结:

从左往右 dp[i+1] = max{dp[i], prices[i+1] - minprices}  ,minprices是区间[0,1,2...,i]内的最低价格

从右往左 dp[i -1] = max{dp[i], maxprices - prices[i-1]}  ,maxprices是区间[i,i+1,...,n-1]内的最高价格。        

posted on 2015-01-25 20:48  NickyYe  阅读(212)  评论(0编辑  收藏  举报