状态机dp,可以参考LeetCode188. 买卖股票的最佳时机 IV。
我们有两个状态,当前交易的天数i和当前是否持有股票,我们用一个二维数组dp[n][2]表示各个状态所能获得的最大收益(其中n为prices数组的大小,表示交易的天数)。
比如dp[i][0]表示第i天(i从0开始),当前不持有股票(第二维0表示不持有股票,1表示持有股票)所能获得的最大收益。
dp[i][1]表示第i天(i从1开始),当前持有股票所能获得的最大收益。
我们考虑一个不持有股票的状态dp[i][0], 不持有股票有两种情况:(1)前一天也不持有股票,然后今天啥也没做,这种情况下收益和前一天是相同的。这种情况下我们有:dp[i][0] = dp[i - 1][0];
(2)前一天持有股票,今天卖掉了,这种情况下我们有:dp[i][0] = dp[i - 1][1] + prices[i]; 其中加prices[i]表示卖掉股票获得了收益。
由于dp数组表示的是某个状态下的最大收益,因此我们需要对上面两种状态取一个max,所以我们有: dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
我们考虑一个持有股票的状态dp[i][1], 持有股票也有两种情况:(1)前一天就持有了股票,然后今天没有操作,这种情况下收益和前一天是相同的。这种情况下我们有:dp[i][1] = dp[i - 1][1];
(2)前一天不持有股票,今天买了一只股票,这种情况下我们有:dp[i][1] = dp[i - 1][1] - prices[i] - fee; 其中减去prices[i]和fee表示第i天以prices[i]的价格买了股票,并交了价格为fee
的手续费。
由于dp数组表示的是某个状态下的最大收益,因此我们需要对上面两种状态取一个max,所以我们有:dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i] - fee);
现在我们有了状态转移方程,由于状态转移的过程中计算dp[i][]需要用到状态dp[i - 1][],当i为0时会导致数组越界,所以我们需要对边界情况特殊处理一下。
dp[0][0]表示第0天,不持有股票所能获得的最大收益,显然dp[0][0] = 0;
dp[0][1]表示第0天,持有股票所能获得的最大收益,显然第0天要持有股票只能第0天买了,要交第0天股票的价格,还有手续费。所以我们有dp[0][1] = -prices[0] - fee;
有了状态表示、处理了递归边界、推导出了状态转移方程,就可以开始状态转移了。
最后的答案就是dp[n - 1][0],表示最后一天,不持有股票所能获得的最高收益。
为什么不是dp[n - 1][1]呢?因为买股票需要成本(股票本身的价格以及手续费),股票卖出去能够获得收入,所以dp[n - 1][0]的值大于dp[n - 1][1]。
代码如下:
class Solution {
public:
int maxProfit(vector<int>& prices, int fee) {
int n = prices.size();
vector<vector<int>> dp(n, vector<int>(2));
for(int i = 0; i < n; ++i) {
if(i == 0) { //递推边界
dp[i][0] = 0;
dp[i][1] = -prices[i] - fee;
continue;
}
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]); //状态转移
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i] - fee);
}
return dp[n - 1][0];
}
};