描述:
給定一個陣列,它的第 i 個元素是一支給定的股票在第 i 天的價格。
設計一個演算法來計算你所能獲取的最大利潤。你最多可以完成 兩筆 交易。
注意: 你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。
示例 1:
輸入: [3,3,5,0,0,3,1,4]
輸出: 6
解釋: 在第 4 天(股票價格 = 0)的時候買入,在第 6 天(股票價格 = 3)的時候賣出,
這筆交易所能獲得利潤 = 3-0 = 3 。
隨後,在第 7 天(股票價格 = 1)的時候買入,在第 8 天 (股票價格 = 4)的時候賣出,
這筆交易所能獲得利潤 = 4-1 = 3 。複製程式碼
示例 2:
輸入: [1,2,3,4,5]
輸出: 4
解釋: 在第 1 天(股票價格 = 1)的時候買入,在第 5 天 (股票價格 = 5)的時候賣出, 這筆交易所能獲得利潤 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接連購買股票,之後再將它們賣出。
因為這樣屬於同時參與了多筆交易,你必須在再次購買前出售掉之前的股票。
複製程式碼
示例 3:
輸入: [7,6,4,3,1]
輸出: 0
解釋: 在這個情況下, 沒有交易完成, 所以最大利潤為 0。複製程式碼
思路:
這是一個經典的動態規劃問題,解決動態規劃問題的步驟是:
- 分析出dp定義
- 列出dp方程
dp定義的分析思路是,思考 i 狀態的產生,和 i - 1狀態有什麼關係,也就是說 i 的狀態,受 i - 1的哪些維度的影響。這裡股票的收益,受前一天股價、是否持股、交易次數有關。所以我們, 可以抽象出三個維度來解決這個問題,即:
dp[ i ][ j ][ k ]:i是天數,j是交易的次數,k是是否持股,我們用這個三維陣列來記錄狀態即可,陣列中存的值,就是當天該狀態下的最大收益。
dp方程需要根據j的取值來分組,即當j = 0 時,不存在 j - 1時的狀態。
當j == 0的時候:
即之前沒有過股票交易,所以是否持股和交易次數都和前一天的狀態相同。
- dp[i][j][0] = Math.max(dp[i - 1][j][0], 0);
- dp[i][j][1] = Math.max(dp[i - 1][j][1], 0);
當j != 0 的時候:
如dp[i][j][0] 第i天,交易了j次,不持股,前一天有兩個狀態可以轉移到該狀態,即:
- 前一天交易了j次,不持股
- 前一天交易了j次,持股但是賣掉了。
- dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);
- dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]);
程式碼:
class Solution {
public int maxProfit(int[] prices) {
if(prices.length == 0) return 0;
int[][][] dp = new int[prices.length][3][2];
dp[0][1][1] = 0 - prices[0];
dp[0][2][1] = 0 - prices[0];
int max = 0;
for (int i = 1; i < prices.length; i++) {
for (int j = 0; j < 3; j++) {
if (j == 0) {
dp[i][j][0] = Math.max(dp[i - 1][j][0], 0);
dp[i][j][1] = Math.max(dp[i - 1][j][1], 0);
} else {
dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);
dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]);
}
}
}
for (int i = 0; i < prices.length; i++) {
max = Math.max(max, dp[i][2][0]);
}
return max;
}
}
複製程式碼