標籤:++ 題意 multi div lan 一個 iii must 有關
題目:
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).
題意及分析:給出一個數組,代表一支股票在某天的價格,求能得到的最大利潤。最多可以進行兩次交易,同時不能同時持有兩支股票。
方法一:因為不能同時持有兩支股票,所以肯定是買入賣出一次交易,然後在買入賣出一次交易。這樣可以把數組分為兩部分,分別求前面的最大利潤和後面部分的最大利潤,加起來就是當前一次的最大利潤,這樣有n種可能,最後遍曆得到最大利潤,因為開始用的是雙重迴圈,所以時間複雜度為0(n^2),提交逾時。這裡可以使用兩次掃描避免雙重迴圈,這樣時間複雜度會降低到o(n)
代碼:
public class Solution { public int maxProfit(int[] prices) { if(prices ==null || prices.length==0 || prices.length ==1) return 0; int[] leftProfit = new int[prices.length]; int minPrice = prices[0]; int maxProfit = 0; for(int i=1;i<prices.length;i++){ //從前向後找,找到到i點的能得到的最大利潤,最大利潤為當前價格減去前面的最小价格和前一個最大利潤之間的較大值 maxProfit = Math.max(maxProfit,prices[i]-minPrice); minPrice = Math.min(minPrice,prices[i]); leftProfit[i] = maxProfit; } int[] rightProfit = new int[prices.length]; int maxPrice = prices[prices.length-1]; maxProfit = 0; for(int i=prices.length-2;i>=0;i--){ //從後向前找,找到到i點的能得到的最大利潤,最大利潤為當前最大價格減去當前價格和前後一個最大利潤之間的較大值 maxProfit = Math.max(maxProfit,maxPrice-prices[i]); maxPrice = Math.max(maxPrice,prices[i]); rightProfit[i] = maxProfit; } maxProfit = 0; for(int i=0;i<prices.length;i++){ maxProfit = Math.max(maxProfit,leftProfit[i]+rightProfit[i]); } return maxProfit; }}
方法二:
第二種解法的核心是假設手上最開始只有0元錢,那麼如果買入股票的價格為price,手上的錢需要減去這個price,如果賣出股票的價格為price,手上的錢需要加上這個price。
它定義了4個狀態:
Buy1[i]表示前i天做第一筆交易買入股票後剩下的最多的錢;
Sell1[i]表示前i天做第一筆交易賣出股票後剩下的最多的錢;
Buy2[i]表示前i天做第二筆交易買入股票後剩下的最多的錢;
Sell2[i]表示前i天做第二筆交易賣出股票後剩下的最多的錢;
那麼Sell2[i]=max{Sell2[i-1],Buy2[i-1]+prices[i]}
Buy2[i]=max{Buy2[i-1],Sell[i-1]-prices[i]}
Sell1[i]=max{Sell[i-1],Buy1[i-1]+prices[i]}
Buy1[i]=max{Buy[i-1],-prices[i]}
可以發現上面四個狀態都是只與前一個狀態有關,所以可以不使用數組而是使用變數來儲存即可。
代碼:
class Solution { public int maxProfit(int[] prices) { if (prices == null || prices.length < 2) return 0; // 四個變數分別表示經過當前操作以後的profit int firstBuy = Integer.MIN_VALUE, firstSell = 0; int secondBuy = Integer.MIN_VALUE, secondSell = 0; for (int curPrice : prices) { firstBuy = Math.max(firstBuy, -curPrice); firstSell = Math.max(firstSell, curPrice + firstBuy); secondBuy = Math.max(secondBuy, firstSell - curPrice); secondSell = Math.max(secondSell, secondBuy + curPrice); } return secondSell; }}
方法三:引用自:http://blog.csdn.net/linhuanmars/article/details/23236995
這裡我們先解釋最多可以進行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];}
[LeetCode] 123. Best Time to Buy and Sell Stock III Java