力扣714-買賣股票的最佳時機含手續費(動態規劃-Java詳細題解)
阿新 • • 發佈:2020-12-21
力扣714-買賣股票的最佳時機含手續費
一、原題題目
1.1 題目
給定一個整數陣列 prices,其中第 i 個元素代表了第 i 天的股票價格 ;非負整數 fee 代表了交易股票的手續費用。你可以無限次地完成交易,但是你每筆交易都需要付手續費。如果你已經購買了一個股票,在賣出它之前你就不能再繼續購買股票了。返回獲得利潤的最大值。
注意:這裡的一筆交易指買入持有並賣出股票的整個過程,每筆交易你只需要為支付一次手續費。
1.2 示例
- 示例一
輸入: prices = [1, 3, 2, 8, 4, 9], fee = 2
解釋: 能夠達到的最大利潤:
在此處買入 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
.
來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee
二、解題思路(動態規劃)
2.1 題目理解
- 題前感慨(時間緊不用看,跳至【言歸正傳】部分) <br />
給定每日股票價格的陣列,每天可以選擇是否買入/賣出,持有時不能再次買入,每筆交易有固定的手續費,求可獲得的最大利潤。給定每日股票價格的陣列,每天可以選擇是否買入/賣出,持有時不能再次買入,每筆交易有固定的手續費,求可獲得的最大利潤。
這是一道入門的動態規劃題目…當說出這句話的時候表示說話者已經對動態規劃有所掌握了,所以能很快的分析出它是一道動態規劃題,並且很容易就能想到什麼狀態變數,轉移方程的所以它是一道簡單的動態規劃題目。但對於很多演算法新手來說,難的是識別它是個動態規劃題。 - 言歸正傳
【1.識別動態規劃】 動態規劃所處理的問題是一個多階段決策問題,一般由初始狀態開始,通過對中間階段決策的選擇,達到結束狀態,此問題當中我們的初試狀態就是第0天開始,結束狀態就是第n天結束。中間的買入/賣出就是中間決策的選擇。
【2.確定狀態定義狀態變數】 知道是動態規劃題後就老老實實的確定狀態和定義狀態變數,如何正確合理的確定狀態也至關重要,例如這裡可以以每天交易結束後手裡是否有股票為狀態,兩種狀態:有股票,沒有股票。我們的目的是求最後的最大利潤的,所以定義狀態變數可以這樣定義:dp[i][0] 表示第 i 天交易結束後手裡沒有股票的最大利潤。dp[i][1] 表示第 i 天交易結束後手裡有股票的最大利潤。
【3.推導狀態轉移方程】 先考慮 dp[i][0] 的轉移方程,第 i 天結束,手裡沒有股票,那麼一定是今天沒有買入。可能的狀態有前一天可能是也沒有股票狀態 dp[i-1][0],或前一天結束時手裡有股票 dp[i-1][1]被我今天賣了。因此轉移方程為:
d p [ i ] [ 0 ] = m a x { d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] − f e e } dp[i][0] = max\{dp[i-1][0],dp[i-1][1]+prices[i]-fee\} dp[i][0]=max{dp[i−1][0],dp[i−1][1]+prices[i]−fee}
在考慮 dp[i][1] 的轉移方程,第 i 天結束後,手裡有股票,那麼一定是今天沒有賣出。可能的狀態有前一天本來就是有股票狀態 dp[i-1][1],或者前一天結束時沒有股票dp[i-1][0],但我今天買入了。因此轉移方程為:
d p [ i ] [ 1 ] = m a x { d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] − p r i c e s [ i ] } dp[i][1] = max\{dp[i-1][1],dp[i-1][0]-prices[i]\} dp[i][1]=max{dp[i−1][1],dp[i−1][0]−prices[i]}
【4.確定邊界】 其實就是確定初始狀態,第0天交易結束時有 dp[0][0] = 0,dp[0][1] = -prices[0]。
2.2 詳細程式碼(Java)
public class Solution {
public int maxProfit(int[] prices, int fee) {
if (prices.length <= 1) return 0;
int[][] dp = new int[prices.length][2]; // 狀態變數,儲存每天有或沒有股票的最大收益
dp[0][0] = 0; // 初始值,第0天沒股票即沒有買入,收益為0
dp[0][1] = -prices[0]; // 初始值,第0天有股票即買入了,收益為-prices[0]
for (int i = 1;i<prices.length;i++){ // 迴圈更新每天的狀態
dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1] + prices[i] - fee);
dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0] - prices[i]);
}
return Math.max(dp[prices.length-1][0],dp[prices.length-1][1]); // 返回最後一天兩種狀態的最大值
}
}
2.3 演算法執行結果
public class Solution {
public int maxProfit(int[] prices, int fee) {
if (prices.length <= 1) return 0;
int nothave = 0; // 初始值,第0天沒股票即沒有買入,收益為0
int have = -prices[0]; // 初始值,第0天有股票即買入了,收益為-prices[0]
for (int i = 1;i<prices.length;i++){ // 迴圈更新狀態值
int n = nothave,y = have; // 下面的更新中都要用到之前的值,所以要臨時儲存一下
nothave = Math.max(n,y + prices[i] - fee);
have = Math.max(y,n - prices[i]);
}
return Math.max(nothave,have); // 返回兩種狀態的最大值
}
}