123. 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).

The thinking is simple and is inspired by the best solution from Single Number II (I read through the discussion after I use DP).
Assume we only have 0 money at first;
4 Variables to maintain some interested 'ceilings' so far:
The maximum of if we've just buy 1st stock, if we've just sold 1nd stock, if we've just buy 2nd stock, if we've just sold 2nd stock.
Very simple code too and work well. I have to say the logic is simple than those in Single Number II.

public class Solution {
    public int maxProfit(int[] prices) {
        int hold1 = Integer.MIN_VALUE, hold2 = Integer.MIN_VALUE;
        int release1 = 0, release2 = 0;
        for(int i:prices){                              // Assume we only have 0 money at first
            release2 = Math.max(release2, hold2+i);     // The maximum if we've just sold 2nd stock so far.
            hold2    = Math.max(hold2,    release1-i);  // The maximum if we've just buy  2nd stock so far.
            release1 = Math.max(release1, hold1+i);     // The maximum if we've just sold 1nd stock so far.
            hold1    = Math.max(hold1,    -i);          // The maximum if we've just buy  1st stock so far. 
        }
        return release2; ///Since release1 is initiated as 0, so release2 will always higher than release1.
    }
}

这里我们先解释最多可以进行k次交易的算法,然后最多进行两次我们只需要把k取成2即可。我们还是使用“局部最优和全局最优解法”。我们维护两种量,一个是当前到达第i天可以最多进行j次交易,最好的利润是多少(global[i][j]),另一个是当前到达第i天,最多可进行j次交易,并且最后一次交易在当天卖出的最好的利润是多少(local[i][j])。下面我们来看递推式,全局的比较简单,

global[i][j]=max(local[i][j],global[i-1][j]),

也就是去当前局部最好的,和过往全局最好的中大的那个(因为最后一次交易如果包含当前天一定在局部最好的里面,否则一定在过往全局最优的里面)。对于局部变量的维护,递推式是

local[i][j]=max(global[i-1][j-1]+max(diff,0),local[i-1][j]+diff),

也就是看两个量,第一个是全局到i-1天进行j-1次交易,然后加上今天的交易,如果今天是赚钱的话(也就是前面只要j-1次交易,最后一次交易取当前天),第二个量则是取local第i-1天j次交易,然后加上今天的差值(这里因为local[i-1][j]比如包含第i-1天卖出的交易,所以现在变成第i天卖出,并不会增加交易次数,而且这里无论diff是不是大于0都一定要加上,因为否则就不满足local[i][j]必须在最后一天卖出的条件了)。
上面的算法中对于天数需要一次扫描,而每次要对交易次数进行递推式求解,所以时间复杂度是O(n*k),如果是最多进行两次交易,那么复杂度还是O(n)。空间上只需要维护当天数据皆可以,所以是O(k),当k=2,则是O(1)。\

public int maxProfit(int[] prices) {  
    if(prices==null || prices.length==0)  
        return 0;  
    int[] local = new int[3];  
    int[] global = new int[3];  
    for(int i=0;i<prices.length-1;i++)  
    {  
        int diff = prices[i+1]-prices[i];  
        for(int j=2;j>=1;j--)  
        {  
            local[j] = Math.max(global[j-1]+(diff>0?diff:0), local[j]+diff);  
            global[j] = Math.max(local[j],global[j]);  
        }  
    }  
    return global[2];  
}  

  

---------------------------------------------------------------------------------------------------------------------------------------------------------------------

动归-- 雅蠛蝶--状态方程:dp[i][j] 表示前i天进行了j次交易, 与前i-1天的关系和最后一天进行了交易的关系, 其实要么是dp[i][j] = max(dp[i-1][j] + ....) 前i-1天进行j次交易, 和前i天进行j次交易,第j次在i交易(另起变量), 要么是dp[i][j] = max(dp[i][j-1] + ...) 和前i天进行了j-1次交易, 因为此时... 不好想, 所以采取前者方法, 而对于local[i][j]=max(global[i-1][j-1]+max(diff,0),local[i-1][j]+diff),:

 


我觉得之所以diff只要考虑prices[i+1]和prices[i],即int diff = prices[i+1]-prices[i],而不需要考虑其他的例如prices[i-1],prices[i-2],prices[i-3]。。。是因为:假如存在一个prices[i-k]使得diff更大更好,那么global[i-1][j-1]必然已经把这个prices[i-k]考虑在计算之中。现在如果我们要再用prices[i-k],就会违反题目里的“You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).”规定。所以唯一还没考虑的就只有prices[i+1]-prices[i]这一对差值。不知道这么说有没问题?

posted @ 2017-08-04 09:00  apanda009  阅读(207)  评论(0编辑  收藏  举报