问题:给你一个整数数组 prices
和一个整数 k
,其中 prices[i]
是某支给定的股票在第 i
天的价格。设计一个算法来计算你所能获取的最大利润。你最多可以完成 k
笔交易。也就是说,你最多可以买 k
次,卖 k
次。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
算法:递归搜索 + 保存计算结果 = 记忆化搜索
在 Leetcode122. 买卖股票 状态机dp C++实现 的基础上,添加一个参数 j,表示当前可以至多交易 j 次。
注意:由于最后未持有股票,手上的股票一定会卖掉,所以代码中的 j-1 可以是在买股票的时候,也可以是在卖股票的时候,这两种写法都是可以的。
以下代码参数 j 的修改时机为买入时。
时间复杂度:O(nk),其中 n 为 prices 的长度。
空间复杂度:O(nk) 。
代码:
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
int n = prices.size();
vector<vector<array<int, 2>>> memo(n, vector<array<int, 2>>(k + 1, {-1, -1})); // -1 表示还没有计算过
auto dfs = [&](auto&& dfs, int i, int j, bool hold) -> int {
if (j < 0) return INT_MIN / 2; // 除 2 防止溢出
if (i < 0) return hold ? INT_MIN / 2 : 0;
int& res = memo[i][j][hold]; // 注意这里是引用
if (res != -1) return res;// 之前计算过
if (hold) return res = max(dfs(dfs, i - 1, j, true), dfs(dfs, i - 1, j - 1, false) - prices[i]);
return res = max(dfs(dfs, i - 1, j, false), dfs(dfs, i - 1, j, true) + prices[i]);
};
return dfs(dfs, n - 1, k, false);
}
};
算法:1:1 翻译成递推
时间复杂度:O(nk),其中 n 为 prices 的长度。
空间复杂度:O(nk) 。
代码:
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
int n = prices.size();
vector<vector<array<int, 2>>> dp(n + 1, vector<array<int, 2>>(k + 2, {INT_MIN / 2, INT_MIN / 2}));
for(int j = 1;j <= k + 1;j++) dp[0][j][0] = 0;
for(int i = 0;i < n;i++)
for(int j = 1;j <= k + 1;j++){
dp[i + 1][j][0] = max(dp[i][j][0],dp[i][j][1] + prices[i]);
dp[i + 1][j][1] = max(dp[i][j][1],dp[i][j - 1][0] - prices[i]);
}
return dp[n][k + 1][0];
}
};
算法:空间优化
必须倒序遍历。
时间复杂度:O(nk),其中 n 为 prices 的长度。
空间复杂度:O(k) 。
代码:
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
vector<array<int, 2>> dp(k + 2, {INT_MIN / 2, INT_MIN / 2});
for(int j = 1;j <= k + 1;j++) dp[j][0] = 0;
for(int p : prices)
for(int j = k + 1;j > 0;j--){
dp[j][0] = max(dp[j][0],dp[j][1] + p);
dp[j][1] = max(dp[j][1],dp[j - 1][0] - p);
}
return dp[k + 1][0];
}
};