188. 买卖股票的最佳时机 IV

动态规划

  1. leetcode🧑‍💻 123. 买卖股票的最佳时机Ⅲ 的基础上,增加了可以有 k 次交易;

  2. 确定 dp 数组以及下标的含义dp[i][j] 表示在 [0, i] 区间里,交易进行了 j 次,拥有的最大现金数;

    1. dp[i][0]:未进行任何操作;
    2. dp[i][1]:第一次买入;
    3. dp[i][2]:第一次卖出;
    4. dp[i][3]:第二次买入;
    5. dp[i][4]:第二次卖出;
    6. dp[i][2k-1]:第 k 次买入;
    7. dp[i][2k]:第 k 次卖出;
  3. 确定递推公式

    1. dp[i][0] = dp[i - 1][0]:未进行任何操作;
    2. 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]:昨天没有持有,今天买入;
    3. 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]:昨天持有,今天卖出
    4. 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]:昨天没有持有,今天买入;
    5. 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]:昨天持有,今天卖出;
  4. 如何初始化 dp 数组

    1. dp[0][0] = 0:第一天没有操作;
    2. dp[0][1] = -prices[0]:第一天第一次买入;
    3. dp[0][2] = 0:当天第一次买入,当天第一次卖出;
    4. dp[0][3] = -prices[0]:第一天第二次买入;
    5. dp[0][4] = 0:当天第二次买入,当天第二次卖出;
  5. 确定遍历顺序从递归公式其实已经可以看出,一定是从前向后遍历,因为 dp[i] 依靠 dp[i - 1] 的数值;

  6. 举例推导 dp 数组:以 prices = [7, 1, 5, 3, 6, 4],k = 2 为例:

    索引 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

复杂度

  1. 时间复杂度:O(nk)

  2. 空间复杂度:状态压缩之前是 O(nk),状态压缩后是 O(k)

实现代码

JavaScript
JavaScript
// 动态规划
const maxProfit = (k, prices) => {
    if (prices == null || prices.length < 2 || k == 0) {
        return 0;
    }

    let dp = new Array(prices.length).fill(0).map(it => new Array(2 * k + 1).fill(0));

    for (let j = 1; j < 2 * k; j += 2) {
        dp[0][j] = 0 - prices[0];
    }

    for (let i = 1; i < prices.length; i++) {
        for (let j = 0; j < 2 * k; j += 2) {
            dp[i][j + 1] = Math.max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);
            dp[i][j + 2] = Math.max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);
        }
    }

    return dp[prices.length - 1][2 * k];
};
// 空间优化
var maxProfit = function (k, prices) {
    let dp = new Array(2 * k + 1).fill(0);

    for (let i = 1; i <= 2 * k; i += 2) {
        dp[i] = -prices[0];
    }

    for (let i = 1; i < prices.length; i++) {
        for (let j = 1; j < 2 * k + 1; j++) {
          if (j % 2) {
                // j 为奇数:买入状态
                dp[j] = Math.max(dp[j], dp[j - 1] - prices[i]);
            } else {
                // j为偶数:卖出状态
                dp[j] = Math.max(dp[j], dp[j - 1] + prices[i]);
            }
        }
    }

    return dp[2 * k];
};

参考资料

  1. 卡尔:《代码随想录》

  2. liweiwei1419

打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

中午好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. 动态规划
    1. 1.1. 复杂度
    2. 1.2. 实现代码
  2. 2. 参考资料