1. 程式人生 > >[Leetcode 動態規劃]買賣股票

[Leetcode 動態規劃]買賣股票

轉自http://liangjiabin.com/blog/2015/04/leetcode-best-time-to-buy-and-sell-stock.html

Best Time to Buy and Sell Stock I

Description: Say you have an array for which the ith element is the price of a given stock on day i. If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find
the maximum profit.

題意:用一個數組表示股票每天的價格,陣列的第i個數表示股票在第i天的價格。 如果只允許進行一次交易,也就是說只允許買一支股票並賣掉,求最大的收益。

分析:動態規劃法。從前向後遍歷陣列,記錄當前出現過的最低價格,作為買入價格,並計算以當天價格出售的收益,作為可能的最大收益,整個遍歷過程中,出現過的最大收益就是所求。

程式碼:O(n)時間,O(1)空間。

  1. publicclass Solution {  
  2.     publicint maxProfit(int[] prices) {  
  3.         if (prices.length < 2) return 0;  
  4.         int
     maxProfit = 0;  
  5.         int curMin = prices[0];  
  6.         for (int i = 1; i < prices.length; i++) {  
  7.             curMin = Math.min(curMin, prices[i]);  
  8.             maxProfit = Math.max(maxProfit, prices[i] - curMin);  
  9.         }  
  10.         return maxProfit;  
  11.     }  
  12. }  

Best Time to Buy and Sell Stock II

Description: 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 as many transactions as you like (ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

題目:用一個數組表示股票每天的價格,陣列的第i個數表示股票在第i天的價格。交易次數不限,但一次只能交易一支股票,也就是說手上最多隻能持有一支股票,求最大收益。

分析:貪心法。從前向後遍歷陣列,只要當天的價格高於前一天的價格,就算入收益。

程式碼:時間O(n),空間O(1)。

  1. publicclass Solution {  
  2.     publicint maxProfit(int[] prices) {  
  3.         if (prices.length < 2) return 0;  
  4.         int maxProfit = 0;  
  5.         for (int i = 1; i < prices.length; i++) {  
  6.             int diff = prices[i] - prices[i - 1];  
  7.             if (diff > 0) {  
  8.                 maxProfit += diff;  
  9.             }  
  10.         }  
  11.         return maxProfit;  
  12.     }  
  13. }  

Best Time to Buy and Sell Stock III

Description: 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).

題意:用一個數組表示股票每天的價格,陣列的第i個數表示股票在第i天的價格。最多交易兩次,手上最多隻能持有一支股票,求最大收益。

分析:動態規劃法。以第i天為分界線,計算第i天之前進行一次交易的最大收益preProfit[i],和第i天之後進行一次交易的最大收益postProfit[i],最後遍歷一遍,max{preProfit[i] + postProfit[i]} (0≤i≤n-1)就是最大收益。第i天之前和第i天之後進行一次的最大收益求法同Best Time to Buy and Sell Stock I。

程式碼:時間O(n),空間O(n)。

  1. publicclass Solution {  
  2.     publicint maxProfit(int[] prices) {  
  3.         if (prices.length < 2) return 0;  
  4.         int n = prices.length;  
  5.         int[] preProfit = newint[n];  
  6.         int[] postProfit = newint[n];  
  7.         int curMin = prices[0];  
  8.         for (int i = 1; i < n; i++) {  
  9.             curMin = Math.min(curMin, prices[i]);  
  10.             preProfit[i] = Math.max(preProfit[i - 1], prices[i] - curMin);  
  11.         }  
  12.         int curMax = prices[n - 1];  
  13.         for (int i = n - 2; i >= 0; i--) {  
  14.             curMax = Math.max(curMax, prices[i]);  
  15.             postProfit[i] = Math.max(postProfit[i + 1], curMax - prices[i]);  
  16.         }  
  17.         int maxProfit = 0;  
  18.         for (int i = 0; i < n; i++) {  
  19.             maxProfit = Math.max(maxProfit, preProfit[i] + postProfit[i]);  
  20.         }  
  21.         return  maxProfit;  
  22.     }  
  23. }  

Best Time to Buy and Sell Stock IV

Description: 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 k transactions. Note: You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

題意:用一個數組表示股票每天的價格,陣列的第i個數表示股票在第i天的價格。最多交易k次,手上最多隻能持有一支股票,求最大收益。

分析:特殊動態規劃法。傳統的動態規劃我們會這樣想,到第i天時進行j次交易的最大收益,要麼等於到第i-1天時進行j次交易的最大收益(第i天價格低於第i-1天的價格),要麼等於到第i-1天時進行j-1次交易,然後第i天進行一次交易(第i天價格高於第i-1天價格時)。於是得到動規方程如下(其中diff = prices[i] – prices[i – 1]):

profit[i][j] = max(profit[i – 1][j], profit[i – 1][j – 1] + diff)

看起來很有道理,但其實不對,為什麼不對呢?因為diff是第i天和第i-1天的差額收益,如果第i-1天當天本身也有交易呢(也就是說第i-1天剛賣出了股票,然後又買入等到第i天再賣出),那麼這兩次交易就可以合為一次交易,這樣profit[i – 1][j – 1] + diff實際上只進行了j-1次交易,而不是最多可以的j次,這樣得到的最大收益就小了。

那麼怎樣計算第i天進行交易的情況的最大收益,才會避免少計算一次交易呢?我們用一個區域性最優解和全域性最有解表示到第i天進行j次的收益,這就是該動態規劃的特殊之處。

我們定義local[i][j]為在到達第i天時最多可進行j次交易並且最後一次交易在最後一天賣出的最大利潤,此為區域性最優。然後我們定義global[i][j]為在到達第i天時最多可進行j次交易的最大利潤,此為全域性最優。它們的遞推式為(其中diff = prices[i] – prices[i – 1])
local[i][j] = max(global[i - 1][j - 1] + max(diff, 0), local[i - 1][j] + diff)
global[i][j] = max(local[i][j], global[i - 1][j]),
其中區域性最優值是比較前一天並少交易一次的全域性最優加上大於0的差值,和前一天的區域性最優加上差值後相比,兩者之中取較大值,而全域性最優比較區域性最優和前一天的全域性最優。

local[i][j]和global[i][j]的區別是:local[i][j]意味著在第i天一定有交易(賣出)發生,當第i天的價格高於第i-1天(即diff > 0)時,那麼可以把這次交易(第i-1天買入第i天賣出)跟第i-1天的交易(賣出)合併為一次交易,即local[i][j]=local[i-1][j]+diff;當第i天的價格不高於第i-1天(即diff<=0)時,那麼local[i][j]=global[i-1][j-1]+diff,而由於diff<=0,所以可寫成local[i][j]=global[i-1][j-1]。global[i][j]就是我們所求的前i天最多進行k次交易的最大收益,可分為兩種情況:如果第i天沒有交易(賣出),那麼global[i][j]=global[i-1][j];如果第i天有交易(賣出),那麼global[i][j]=local[i][j]。

參考:http://www.cnblogs.com/grandyang/p/4295761.html

程式碼:時間O(nk),空間O(nk)。

  1. publicclass Solution {  
  2.     publicint maxProfit(int k, int[] prices) {  
  3.         if (prices.length < 2) return 0;  
  4.         int days = prices.length;  
  5.         if (k >= days) return maxProfit2(prices);  
  6.         int[][] local = newint[days][k + 1];  
  7.         int[][] global = newint[days][k + 1];  
  8.         for (int i = 1; i < days ; i++) {  
  9.             int diff = prices[i] - prices[i - 1];  
  10.             for (int j = 1; j <= k; j++) {  
  11.                 local[i][j] = Math.max(global[i - 1][j - 1], local[i - 1][j] + diff);  
  12.                 global[i][j] = Math.max(global[i - 1][j], local[i][j]);  
  13.              }  
  14.         }  
  15.         return global[days - 1][k];  
  16.     }  
  17.     publicint maxProfit2(int[] prices) {  
  18.         int maxProfit = 0;  
  19.         for (int i = 1; i < prices.length; i++) {  
  20.             if (prices[i] > prices[i - 1]) {  
  21.                 maxProfit += prices[i] - prices[i - 1];  
  22.             }  
  23.         }  
  24.         return maxProfit;  
  25.     }  
  26. }  

我們知道,動規所用的二維輔助陣列可以降為一維的,即只用大小為k的一維陣列記錄到達第i天時的區域性最優解和全域性最優解。需要注意的是,由於第i天時交易k次的最優解依賴於第i-1天時交易k-1次的最優解,所以陣列更新應當從後往前(即從k到1)更新。

程式碼:時間O(nk),空間O(k)。

  1. publicclass Solution {  
  2.     publicint maxProfit(int k, int[] prices) {  
  3.         if (prices.length < 2) return 0;  
  4.         if (k >= prices.length) return maxProfit2(prices);  
  5.         int[] local = newint[k + 1];  
  6.         int[] global = newint[k + 1];  
  7.         for (int i = 1; i < prices.length ; i++) {  
  8.             int diff = prices[i] - prices[i - 1];  
  9.             for (int j = k; j > 0; j--) {  
  10.                 local[j] = Math.max(global[j - 1], local[j] + diff);  
  11.                 global[j] = Math.max(global[j], local[j]);  
  12.             }  
  13.         }       
  14.         return global[k];  
  15.     }     
  16.     publicint maxProfit2(int[] prices) {  
  17.         int maxProfit = 0;  
  18.         for (int i = 1; i < prices.length; i++) {  
  19.             if (prices[i] > prices[i - 1]) {  
  20.                 maxProfit += prices[i] - prices[i - 1];  
  21.             }  
  22.         }  
  23.         return maxProfit;  
  24.     }  
  25. }  

補充:這道題還有一個陷阱,就是當k大於天數時,其實就退化成 Best Time to Buy and Sell Stock II 了。 另外,Best Time to Buy and Sell Stock III 就是本題k=2的情況,所以說IV是II和III的綜合。

309. Best Time to Buy and Sell Stock with Cooldown

在第二題(次數不限)的基礎上,賣完股票的轉天不能買。求最大收益。

分析:http://www.cnblogs.com/grandyang/p/4997417.html

維護三個一維陣列buy, sell,和rest。其中:
buy[i]表示在第i天之前最後一個操作是買,此時的最大收益。
sell[i]表示在第i天之前最後一個操作是賣,此時的最大收益。
rest[i]表示在第i天之前最後一個操作是冷凍期,此時的最大收益。
我們寫出遞推式為:

  1. buy[i]  = max(rest[i-1] - price, buy[i-1])   
  2. sell[i] = max(buy[i-1] + price, sell[i-1])  
  3. rest[i] = max(sell[i-1], buy[i-1], rest[i-1])  
上述遞推式很好的表示了在買之前有冷凍期,買之前要賣掉之前的股票