给定一个整数数组prices,其中prices[i]表示第i天的股票价格,整数fee代表了交易股票的手续费用。你可以无限次地完成交易,但是你每笔交易都需要付手续费。这里的一笔交易指买入持有到卖出的整个过程,每笔交易你只需要支付一次手续费。如果你已经购买了一个股票,在卖出它之前你就不能再购买股票了。请计算你能获得的最大利润。
要分清楚状态和操作,我会用「」把状态和操作都标记出来。注意到,第i天结束后,手里有可能有股票,也就是处于「买入」状态;手里也有可能没有股票,也就是处于「卖出」状态。对于前者,接下来可以执行的操作有「持有股票」和「卖出股票」;对于后者,接下来可以执行的操作有「观望」和「买入股票」。在进行「买入股票」和「卖出股票」时会引起利润变化,前者减少利润,后者增加利润。「卖出股票」时,进行了一笔完整的交易,要缴纳手续费。
我们用动态规划的思想来解决这个问题。首先确定状态表示。我们用f表示处于「买入」状态,g表示处于「卖出」状态,i表示时间处于第i天结束时,表中的值表示最大利润。也就是说:
- f[i]表示在第i天结束时,处于「买入」状态,此时能获得的最大利润。
- g[i]表示在第i天结束时,处于「卖出」状态,此时能获得的最大利润。
接下来推导状态转移方程:
考虑f[i]。在第i天结束时处于「买入」状态,分类讨论:
- 如果第i-1天结束时处于「买入」状态,并且在第i天「持有股票」,有f[i]=f[i-1]。
- 如果第i-1天结束时处于「卖出」状态,并且在第i天「买入股票」,有f[i]=g[i-1]-prices[i]。
考虑g[i]。在第i天结束时处于「卖出」状态,分类讨论:
- 如果第i-1天结束时处于「卖出」状态,并且在第i天「观望」,有g[i]=g[i-1]。
- 如果第i-1天结束时处于「买入」状态,并且在第i天「卖出股票」,有g[i]=f[i-1]+prices[i]-fee。
综上,有:f[i]=max(f[i-1],g[i-1]-prices[i]),g[i]=max(g[i-1],f[i-1]+prices[i]-fee)。
接着考虑初始化的问题。
- 对于f[0],要在第0天结束时处于「买入」状态,就要在第0天「买入股票」,所以f[0]=-prices[0]。
- 对于g[0],要在第0天结束时处于「卖出」状态,就要在第0天「观望」,所以g[0]=0。
填表时应按照从左往右的顺序,同时填f表和g表。最后应返回g[n-1],因为只有最后一天结束时处于「卖出」状态,才有可能获得最大利润。
cpp
class Solution {
public:
int maxProfit(vector<int>& prices, int fee) {
int n = prices.size();
// 创建dp表
vector<int> f(n);
auto g = f;
// 初始化g[0]=0
f[0] = -prices[0];
// 填表
for (int i = 1; i < n; i++)
{
f[i] = max(f[i-1], g[i-1] - prices[i]);
g[i] = max(g[i-1], f[i-1] + prices[i] - fee);
}
return g[n-1];
}
};