题目来源:力扣。
714. 买卖股票的最佳时机含手续费
给定一个整数数组 prices
,其中 prices[i]
表示第 i
天的股票价格 ;整数 fee
代表了交易股票的手续费用。
你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。
返回获得利润的最大值。
**注意:**这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。
示例 1:
输入: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
示例 2:
输入:prices = [1,3,7,5,10,3], fee = 3
输出:6
提示:
- 1 < = p r i c e s . l e n g t h < = 5 ∗ 1 0 4 1 <= prices.length <= 5 * 10^4 1<=prices.length<=5∗104
- 1 < = p r i c e s [ i ] < 5 ∗ 1 0 4 1 <= prices[i] < 5 * 10^4 1<=prices[i]<5∗104
- 0 < = f e e < 5 ∗ 1 0 4 0 <= fee < 5 * 10^4 0<=fee<5∗104
思路:(动态规划)
考虑到「不能同时参与多笔交易」,因此每天交易结束后只可能存在手里 有一支股票 或者 没有股票 的状态,所以每天交易结束后,一共有 两种状态 :
- 有一支股票
- 没有股票
我们设dp[i]
表示第 i
天交易结束后的 「获得利润的最大值」,对应 两种不同的状态, 分别用 dp[i][0]
、dp[i][1]
,对应的两种状态为:
- 第
i
天交易结束后有一支股票,有两种可能到达该状态:- 第
i − 1
天就已经持有,即dp[i][0]
=dp[i - 1][0]
- 或者第
i
天刚买入,那么第i−1
天就一定没有股票,即dp[i][0]
=dp[i - 1][1]
-prices[i]
- 第
- 第
i
天交易结束后没有股票,也有两种可能到达该状态:- 第
i − 1
就没有股票, 即:dp[i][1]
=dp[i - 1][1]
- 第
i − 1
有一支股票,但是再第i
天卖出了, 即:dp[i][1]
=dp[i - 1][0]
+prices[i]
-fee
- 第
这样我们就得到了所有的状态转移方程。如果一共有 n 天,那么最终的答案即为:
m
a
x
(
d
p
[
n
−
1
]
[
0
]
,
d
p
[
n
−
1
]
[
1
]
)
max(dp[n - 1][0], dp[n - 1][1])
max(dp[n−1][0],dp[n−1][1])
空间优化:
- 注意到在状态转移方程中,
dp[i][0]
和dp[i][1]
只会从dp[i−1][0]
和dp[i−1][1]
转移而来,因此我们不必使用数组存储所有的状态,而是使用两个变量have
分别表示nhave
直接进行状态转移即可。
代码:(Java、C++)
动态数组:(Java)
public class MaxProfit_fee {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] prices = {1, 3, 2, 8, 4, 9};
int fee = 2;
System.out.println(maxProfit(prices, fee));
}
public static int maxProfit(int[] prices, int fee) {
if(prices.length == 1) {
return 0;
}
int n = prices.length;
int[][] dp = new int[n][2];//表示每天交易结束后,能获得的最大收益
dp[0][0] = -prices[0];
for(int i = 1; i < n; i++) {
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee);
}
return Math.max(dp[n - 1][0], dp[n - 1][1]);
}
}
空间优化:
(Java)
public class MaxProfit_fee {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] prices = {1, 3, 2, 8, 4, 9};
int fee = 2;
System.out.println(maxProfit(prices, fee));
}
public static int maxProfit(int[] prices, int fee) {
if(prices.length == 1) {
return 0;
}
int have = -prices[0];
int nhave = 0 ,tem ;
for(int price : prices) {
tem = have;
have = Math.max(have, nhave - price);
nhave = Math.max(nhave, tem + price - fee);
}
return Math.max(have, nhave);
}
}
C++
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int maxProfit(vector<int>& prices, int fee) {
if (prices.size() == 1) {
return 0;
}
vector<int>::iterator it = prices.begin();
int have = -(*it);
int nhave = 0, tem;
while (++it != prices.end()) {
tem = have;
have = max(have, nhave - (*it));
nhave = max(nhave, tem + (*it) - fee);
}
return max(have, nhave);
}
int main(){
vector<int> prices = { 1, 3, 2, 8, 4, 9 };
int fee = 2;
cout << maxProfit(prices, fee) << endl;
return 0;
}
运行结果:
复杂度分析:
- 时间复杂度: O ( n ) O(n) O(n),其中 n 为数组的长度,需要遍历一遍。
- 空间复杂度: O ( 1 ) O(1) O(1), 空间优化前是 O ( n ) O(n) O(n),优化后是 O ( 1 ) O(1) O(1)。