714. Best Time to Buy and Sell Stock with Transaction Fee
題目描述
Your are given an array of integers prices, for which the i-th element is the price of a given stock on day i; and a non-negative integer fee representing a transaction fee.
You may complete as many transactions as you like, but you need to pay the transaction fee for each transaction. You may not buy more than 1 share of a stock at a time (ie. you must sell the stock share before you buy again.)
Return the maximum profit you can make.
Example 1:
Input: prices = [1, 3, 2, 8, 4, 9], fee = 2
Output: 8
Explanation: The maximum profit can be achieved by:
Buying at prices[0] = 1
Selling at prices[3] = 8
Buying at prices[4] = 4
Selling at prices[5] = 9
The total profit is ((8 - 1) - 2) + ((9 - 4) - 2) = 8.
Note:
- 0 < prices.length <= 50000.
- 0 < prices[i] < 50000.
- 0 <= fee < 50000.
解題思路
顯然,這是一道動態(tài)規(guī)劃問題。對于這種問題而言,理解問題,并抽離出模型尤為關(guān)鍵。從題目可以知道,我們在每一天可以買、賣股票,或者什么都不做,但是約束是手里持有股票的股票數(shù)量最多為1(不能一次買多股,買之前必須先賣掉)。每次交易我們都需要付出一定的手續(xù)費。算出最后一天最大的利潤值。
先從最后一條規(guī)則入手:買賣股票需要付手續(xù)費,也就是說,如果在當(dāng)天同時買入又賣出股票,最后得到的利潤是負(fù)數(shù),顯然是不符合題目要求的,因此,我們可以得出每天的操作只能是:買一次、賣一次、什么都不做。另外,如果我們要在某一天賣掉股票,由于當(dāng)天買入立即賣出是不可行的,因此,在前一天,我們至少要持有股票。總結(jié)一下就是:
今天賣股票之后的利潤=前一天手握這支股票時的利潤+今天股票的價格-手續(xù)費
同理,如果今天我們需要買進股票:
今天買股票之后的利潤=前一天沒有股票時的利潤-今天股票的價格
最后,如果今天什么都不做:
今天持有股票的利潤=昨天持有股票的利潤
今天沒有股票的利潤=昨天沒有股票的利潤
從上述的式子,我們不難總結(jié)出:每天的狀態(tài)都分為是否持有股票兩種情況。題目要求我們求出最后一天(N)能獲得的最大利潤,我們不妨可以這樣考慮:
最后一天持有股票的利潤=前一天持有股票的利潤 | 前一天沒有股票的利潤-今天的價格
最后一天沒有股票的利潤=前一天沒有股票的利潤 | 前一天持有股票的利潤+今天的價格-手續(xù)費
假設(shè)這時候我們已經(jīng)知道了第N-1天沒有持有股票時利潤的最大值以及持有股票時利潤的最大值,那么最后一天的持有股票和沒有股票的利潤的最大值就是:
最后一天持有股票的利潤=MAX{前一天持有股票的利潤, 前一天沒有股票的利潤-今天的價格}
最后一天沒有股票的利潤=MAX{前一天沒有股票的利潤, 前一天持有股票的利潤+今天的價格-手續(xù)費}
第一天兩種狀態(tài)的利潤是已知的,因此,無論N取什么值,都能算出當(dāng)前N兩種狀態(tài)的最大值。由此總結(jié)出動態(tài)規(guī)劃的狀態(tài)轉(zhuǎn)移方程:
// 0代表沒有股票,1代表持有股票
profit[n][0] = max(profit[n-1][1] + price[n] - fee, profit[n-1][0])
profit[n][1] = max(profit[n-1][1], profit[n-1][0] - price[n])
return max(profit[n][0], profit[n][1])
一點優(yōu)化
由于我們并不需要返回每天的最大利潤,因此,記錄下每天的狀態(tài)和價格是沒有必要的,只需要記錄下前一天的兩個狀態(tài),并且在今天的迭代中分別替換掉他們即可。
時間復(fù)雜度分析
每天的計算量為O(1),遍歷一次即可得到結(jié)果,復(fù)雜度為O(n)。
空間復(fù)雜度分析
如果不做優(yōu)化,則需要開一個天數(shù)乘二大小的二維數(shù)組,復(fù)雜度為O(n)。優(yōu)化后復(fù)雜度為O(1).
源碼
class Solution {
public:
int maxProfit(vector<int>& prices, int fee) {
if (prices.size() == 0) {
return 0;
}
int days = prices.size();
int profits[50001][2] = {0};
profits[0][1] = -prices[0];
for (int i = 1; i < days; ++i) {
profits[i][0] = max(profits[i-1][1] + prices[i] - fee, profits[i-1][0]);
profits[i][1] = max(profits[i-1][1], profits[i-1][0] - prices[i]);
}
return profits[days-1][0] > profits[days-1][1] ? profits[days-1][0] : profits[days-1][1];
}
};