题目理解
我们要在给定的股票价格数组 prices
中进行买卖操作,并尽可能多次交易以获取最大利润。每次交易都需要支付一定的手续费 fee
,因此我们必须考虑如何通过合适的交易策略最大化利润。
在本题中,每一天可以选择:
- 不进行任何操作。
- 买入股票。
- 卖出股票(前提是已经持有股票)。
动态规划思路
我们可以使用动态规划(DP)来解决这个问题。在动态规划中,我们定义两种状态:
- 持有股票状态(持仓):
hold[i]
表示第i
天结束时持有股票时的最大利润。 - 不持有股票状态(空仓):
cash[i]
表示第i
天结束时不持有股票时的最大利润。
状态转移方程
-
持有股票的状态:
我们有两种可能:- 我们在第
i
天之前已经持有股票,那么hold[i]
就和hold[i-1]
相同。 - 我们在第
i
天买入股票,那么需要从cash[i-1]
(前一天不持有股票的最大利润)中减去prices[i]
(当天股票价格)。
因此,持有股票状态的转移方程为:
h o l d [ i ] = max ( h o l d [ i − 1 ] , c a s h [ i − 1 ] − p r i c e s [ i ] ) hold[i] = \max(hold[i-1], cash[i-1] - prices[i]) hold[i]=max(hold[i−1],cash[i−1]−prices[i])
- 我们在第
-
不持有股票的状态:
我们有两种可能:- 我们在第
i
天之前已经卖出了股票,那么cash[i]
就和cash[i-1]
相同。 - 我们在第
i
天卖出股票,此时需要加上prices[i]
的收入并扣除手续费fee
。
因此,不持有股票状态的转移方程为:
c a s h [ i ] = max ( c a s h [ i − 1 ] , h o l d [ i − 1 ] + p r i c e s [ i ] − f e e ) cash[i] = \max(cash[i-1], hold[i-1] + prices[i] - fee) cash[i]=max(cash[i−1],hold[i−1]+prices[i]−fee)
- 我们在第
初始状态
hold[0] = -prices[0]
:第0天如果买入股票,我们的利润就是负的第0天的股票价格。cash[0] = 0
:第0天如果不买股票,利润为0。
最终结果
我们最终需要的是在最后一天结束时,不持有股票时的最大利润,即 cash[n-1]
,其中 n
是 prices
的长度。
C++ 实现
#include <vector>
#include <algorithm>
using namespace std;
int maxProfit(vector<int>& prices, int fee) {
int n = prices.size();
vector<int> hold(n), cash(n);
hold[0] = -prices[0]; // 第 0 天持有股票
cash[0] = 0; // 第 0 天不持有股票
for (int i = 1; i < n; ++i) {
cash[i] = max(cash[i-1], hold[i-1] + prices[i] - fee); // 不持有股票
hold[i] = max(hold[i-1], cash[i-1] - prices[i]); // 持有股票
}
return cash[n-1]; // 最后一天不持有股票的最大利润
}
优化思路
这个基本解法需要两个数组 hold
和 cash
,分别存储持有和不持有股票时的利润值。这会占用 O(n)
的空间。而实际上,在计算第 i
天的状态时,只依赖于 i-1
天的状态,因此我们可以使用两个变量代替数组,优化空间复杂度到 O(1)
。
优化后的实现
#include <vector>
#include <algorithm>
using namespace std;
int maxProfit(vector<int>& prices, int fee) {
int n = prices.size();
int hold = -prices[0]; // 持有股票时的最大利润
int cash = 0; // 不持有股票时的最大利润
for (int i = 1; i < n; ++i) {
cash = max(cash, hold + prices[i] - fee); // 更新不持有股票的最大利润
hold = max(hold, cash - prices[i]); // 更新持有股票的最大利润
}
return cash; // 最后一天不持有股票的最大利润
}
解释及示例
示例 1
输入:prices = [1, 3, 2, 8, 4, 9]
, fee = 2
输出:8
过程:
- 第 0 天:买入股票,
hold = -1
,cash = 0
。 - 第 1 天:卖出股票,
cash = max(0, -1 + 3 - 2) = 0
,保持持有状态,hold = -1
。 - 第 2 天:保持持有状态,
cash = 0
,hold = max(-1, 0 - 2) = -1
。 - 第 3 天:卖出股票,
cash = max(0, -1 + 8 - 2) = 5
,hold = max(-1, 5 - 8) = -1
。 - 第 4 天:买入股票,
cash = 5
,hold = max(-1, 5 - 4) = 1
。 - 第 5 天:卖出股票,
cash = max(5, 1 + 9 - 2) = 8
,hold = 1
。
最终结果:cash = 8
。
示例 2
输入:prices = [1, 3, 7, 5, 10, 3]
, fee = 3
输出:6
- 第 0 天:买入股票,持有股票的利润为
hold = -1
,不持有股票的利润为cash = 0
。 - 第 1 天:卖出股票后利润为
cash = max(0, -1 + 3 - 3) = 0
,持有状态hold = max(-1, 0 - 3) = -1
。 - 第 2 天:卖出股票后利润为
cash = max(0, -1 + 7 - 3) = 3
,持有状态hold = max(-1, 3 - 7) = -1
。 - 第 3 天:保持不持有状态
cash = max(3, -1 + 5 - 3) = 3
,持有状态hold = max(-1, 3 - 5) = -1
。 - 第 4 天:卖出股票后利润为
cash = max(3, -1 + 10 - 3) = 6
,持有状态hold = max(-1, 6 - 10) = -1
。 - 第 5 天:保持不持有状态
cash = max(6, -1 + 3 - 3) = 6
。
最终利润为 6。
关键点总结
-
最优子结构:第
i
天的状态只取决于第i-1
天的状态。 -
状态转移方程:
- 持有状态:
hold[i] = max(hold[i-1], cash[i-1] - prices[i])
- 不持有状态:
cash[i] = max(cash[i-1], hold[i-1] + prices[i] - fee)
- 持有状态:
-
空间优化:我们只需要两个变量
hold
和cash
,可以将空间复杂度从O(n)
优化到O(1)
。