給定一個整數數組 prices,其中第 i 個元素代表了第 i 天的股票價格 ;非負整數 fee 代表了交易股票的手續費用。
你可以無限次地完成交易,但是你每次交易都需要付手續費。如果你已經購買了一個股票,在賣出它之前你就不能再繼續購買股票了。
返回獲得利潤的最大值。
示例 :
輸入: prices = [1, 3, 2, 8, 4, 9], fee = 2
輸出: 8
解釋: 能夠達到的最大利潤:
在此處買入 prices[0] = 1
在此處賣出 prices[3] = 8
在此處買入 prices[4] = 4
在此處賣出 prices[5] = 9
總利潤: ((8 - 1) - 2) + ((9 - 4) - 2) = 8.
注意:
- 0 < prices.length <= 50000.
- 0 < prices[i] < 50000.
- 0 <= fee < 50000.
思路:
- 動態規劃求解效率最高
- 需要多個狀態轉移方程
- 主要學習大神代碼,傳送門
代碼:
public int maxProfit(int[] prices, int fee) {
int n = prices.length;
if (n < 2) {
return 0;
}
int[] hold = new int[n];
int[] notHold = new int[n];
hold[0] = -prices[0];
for (int i = 1; i < n; ++i) {
hold[i] = Math.max(hold[i - 1], notHold[i - 1] - prices[i]);
notHold[i] = Math.max(notHold[i - 1], hold[i - 1] + prices[i] - fee);
}
return notHold[n - 1];
}
詳解:
- 由于大神只貼了代碼,需要自己去學習理解,這里貼出自己的看法.
- 本題是買賣股票問題的擴展版,可以多次進行交易,但是每次完整的交易(買入+賣出)都需要支付手續費,且同一時間只能買一只股票,由此得到結論:
a. 賣出-買入>fee
才有利潤
b. 盡量減少買賣次數 - 求得動態轉移方程,此處使用雙方程的方式進行求解,其中
hold[]
存儲當前節點本金,即手里有多少錢,初始化值為-prices[0]
,默認設置買下第一只股票,此時手里欠款-prices[0]
.
notHold[]
存儲當前節點所獲得的最大利潤,即本題的返回值 - 狀態轉移方程實現:
hold[i] = Math.max(hold[i - 1], notHold[i - 1] - prices[i]);
設置本金為前一節點的本金與前節點最大利潤減去當前股票值的最大值,即將該節點設置為新的股票購入節點,或者忽略該節點.本金數組主要解決股票購入時機的問題. - 狀態轉移方程實現:
notHold[i] = Math.max(notHold[i - 1], hold[i - 1] + prices[i] - fee);
設置最大利潤為前一節點最大利潤與本金+當前節點賣出價值-fee的最大值,即在當前節點進行股票變現,或者忽略該節點,最大利潤數組主要解決股票賣出時機問題. - 變化表格:
股票價格 | 本金(hold) | 最大利潤(notHold) |
---|---|---|
1 | -1 | 0 |
3 | -1 | 0 |
2 | -1 | 0 |
8 | -1 | 5 |
4 | 1 | 5 |
9 | 1 | 8 |
在第一只股票時買入,在第四只股票是賣出,獲利5.
在第5只股票時買入,此時本金為5-4=1 > -1,賦值為1,在第六只股票時賣出,得到最大利潤值為8
分析:
- 設置本金狀態轉移方程為
E(n) = max(E(n-1),F(n-1)-prices[n]),只有當該節點之前的節點進行過股票出售,F(n-1)-prices[n]才會大于E(n-1)
設置最大利潤狀態轉移方程為
F(n) = max (F(n-1), E(n-1)+prices[n]-fee),使用本金+當前節點出售股票獲利進行比較,比前節點更多獲利才會進行結算 - 本金邊界為 E(0),此時F(n-1)不存在,所以直接賦值-prices[0] . 總利潤邊界為F(1) ,只有買入股票后才能出售,當在此節點出售時,使用 prices[1]-prices[0]-fee.
總結:
- 瞎碼字,以后再補充吧
- 動態規劃問題需要很好的讀題+總結歸納能力.
- 該問題中,核心方程為總利潤的狀態轉移方程,若能求出該方程,即可推導出本金是一個變化的過程,需要另寫數組存儲 -> 得到第二個狀態轉移方程
- 再見到類似題可能還是做不出來,哎好氣