【LeetCode-188】买卖股票的最佳时机 IV
问题
给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例
输入: k = 2, prices = [2,4,1]
输出: 2
解释: 在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。
解答1:完整状态机
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
int n = prices.size();
vector<vector<int>> hold(n + 1, vector<int>(k + 1, INT_MIN));
vector<vector<int>> sold(n + 1, vector<int>(k + 1, 0));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= k; j++) {
sold[i][j] = max(sold[i - 1][j], hold[i - 1][j] + prices[i - 1]);
hold[i][j] = max(hold[i - 1][j], sold[i - 1][j - 1] - prices[i - 1]);
}
}
return sold[n][k];
}
};
重点思路
本题一共有三个状态:天数、可购买次数、持有和未持有状态。首先不考虑状态压缩问题,直接写出三维dp数组dp[i][j][k]
。
接下来考虑状态转移方程问题,我们需要对每一个状态维度遍历所有可能取值,也就是需要三层for循环,但是“持有和未持有状态”只包含两个取值,所以可以直接拆开,最终只需要两个for循环。我们将这两个取值分别定义为hold
(持有)和sold
(未持有),具体的状态转移方程可写为:sold[i][j] = max(sold[i - 1][j], hold[i - 1][j] + prices[i])
和hold[i][j] = max(hold[i - 1][j], sold[i - 1][j - 1] - prices[i]);
。为了避免复杂的初始化,我们在天数维度上考虑“第0天”。
然后是边界条件的初始化问题:
sold
初始化:第0天必定是未持有状态,利润为0,并且后续未持有状态的最小利润必定大于0,所以可以将sold
全部初始化为0;hold
初始化:第0天为hold
的无效状态,并且后续需要求最大值,所以全部初始化为INT_MIN
。
解答2:状态压缩
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
vector<int> hold(k + 1, INT_MIN);
vector<int> sold(k + 1, 0);
for (int p : prices) {
for (int i = 1; i <= k; i++) {
sold[i] = max(sold[i], hold[i] + p);
hold[i] = max(hold[i], sold[i - 1] - p);
}
}
return sold[k];
}
};
重点思路
根据解答1中的状态转移方程可知,天数这个维度每次都只考虑了上一天的值,所以这一个维度可以去掉。最终得到只包含两个一维dp数组的动态规划算法。
初始化方法同解答1。