动态规划
解题思路
确定 dp 数组以及下标的含义:dp[i][j] 表示在 [0, i] 区间里,交易进行了 j 次,拥有的最大现金数;
- dp[i][0]:未进行任何操作;
- dp[i][1]:只进行过一次买的操作;
- dp[i][2]:只进行了一次买操作、一次卖操作,即完成了一笔交易;
- dp[i][3]:进行第二次买操作;
- dp[i][4]:完成了全部两笔交易;
确定递推公式:
- dp[i][0] = dp[i - 1][0]:未进行任何操作;
- dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]):只进行过一次买的操作;
- dp[i - 1][1]:昨天持有,今天不操作;
- dp[i - 1][0] - prices[i]:昨天没有持有,今天买入;
- dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + prices[i]):只进行了一次买操作、一次卖操作,即完成了一笔交易;
- dp[i - 1][2]:昨天卖出,今天不操作;
- dp[i - 1][1] + prices[i]:昨天持有,今天卖出
- dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - prices[i]):进行第二次买操作;
- dp[i - 1][3]:昨天持有,今天不操作;
- dp[i - 1][2] - prices[i]:昨天没有持有,今天买入;
- dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + prices[i]):完成了全部两笔交易;
- dp[i - 1][4]:昨天卖出,今天不操作;
- dp[i - 1][3] + prices[i]:昨天持有,今天卖出;
如何初始化 dp 数组:
- dp[0][0] = 0:第一天没有操作;
- dp[0][1] = -prices[0]:第一天第一次买入;
- dp[0][2] = 0:当天第一次买入,当天第一次卖出;
- dp[0][3] = -prices[0]:第一天第二次买入;
- dp[0][4] = 0:当天第二次买入,当天第二次卖出;
确定遍历顺序:从递归公式其实已经可以看出,一定是从前向后遍历,因为 dp[i] 依靠 dp[i - 1] 的数值;
举例推导 dp 数组:以 prices = [7, 1, 5, 3, 6, 4] 为例:
索引 i = 0 索引 i = 1 索引 i = 2 索引 i = 3 索引 i = 4 索引 i = 5 dp[i][0] 规定了今天 不持股 0 0 0 0 0 0 dp[i][1] 规定了今天 持股 -7 -1 -1 -1 -1 -1 dp[i][2] 规定了今天 不持股 0 0 4 4 5 5 dp[i][3] 规定了今天 持股 -7 -1 -1 1 1 1 dp[i][4] 规定了今天 不持股 0 0 4 4 7 7
复杂度
-
时间复杂度:
O(n)
; -
空间复杂度:状态压缩之前是
O(n)
,状态压缩后是O(1)
;
代码实现
// 时间复杂度 O(n) 空间复杂度 O(n),dp 数组第二维是常数
var maxProfit = function (prices) {
let dp = new Array(prices.length).fill(0).map(it => new Array(5).fill(0));
dp[0][0] = 0;
dp[0][1] = -prices[0];
dp[0][2] = 0;
dp[0][3] = -prices[0];
dp[0][4] = 0;
for (let i = 1; i < prices.length; i++) {
dp[i][0] = dp[i - 1][0];
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + prices[i]);
dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
}
return dp[prices.length - 1][4];
}
// 状态压缩
var maxProfit = function (prices) {
let dp = [0, -prices[0], 0, -prices[0], 0];
for (let i = 1; i < prices.length; i++) {
dp[1] = Math.max(dp[1], dp[0] - prices[i]);
dp[2] = Math.max(dp[2], dp[1] + prices[i]);
dp[3] = Math.max(dp[3], dp[2] - prices[i]);
dp[4] = Math.max(dp[4], dp[3] + prices[i]);
}
return dp[4];
};
参考资料
leetcode🧑💻 122. 买卖股票的最佳时机Ⅱ
上一篇