一天一大 leet(最佳买卖股票时机含冷冻期)难度:中等-Day20200710

一天一大 leet(最佳买卖股票时机含冷冻期)难度:中等-Day20200710_第1张图片

题目:

给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。​

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

  • 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
  • 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

示例

输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]

抛砖引玉

一天一大 leet(最佳买卖股票时机含冷冻期)难度:中等-Day20200710_第2张图片

  • 第一天收益设置为 -prices[0],作为成本(即成本也纳入收益计算)
  • 第 i 天价格为 prices[i]
  • 冷冻期 如果这一天为冷冻期说明为前一天卖出,则这一天的收益为:
    • 前一天持有时的最大收益+卖出的盈利
    • dp[i-1][持有] + prices[i]
  • 不持有 如果这一天不持有则前一天可能是不持有或者为冷冻期(一定不是持有),则这一天收益理论上不变沿用前一天的收益
    计算收益最大则取两周可能中较大的收益 max:
    • max(dp[i-1][不持有], dp[i-1][冷冻])
  • 持有 如果这一天为持有,则前一天可能是持有也可能为不持有(一定不是冷冻),则这一天收益:
    • 注意 前一天如果为不持有则说明今天买入后才持有则按照计入成本规则需要减掉今日的成本
    • max(前一天持有时的最大收益, 前一天不持有时的最大收益-今日成本)
    • max(dp[i-1][持有], dp[i-1][不持有]-prices[i])

示例:[1,2,3,0,2]

价格 p(i)\收益 dp[i] i=0,p[0]=1 i=1,p[1]=2 i=2,p[2]=3 i
持有 0 0-1 max(0-1,0-2) max(-1,0-3) max(dp[i-1][0], dp[i-1][2] - p[i])
冷冻期 1 - 0-1+2 -1+3 dp[i-1][0]+p[i]
不持有 2 0 max(0,-) max(0,1) max(dp[i-1][2], dp[i-1][1])

最后如果计算收益最大,则结束时状态不能为持有

/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function (prices) {
  let len = prices.length,
    dp = Array(len)
  // prices小于2是买入来不及卖出
  if (len < 2) return 0
  // 初始化存储对象
  for (let i = 0; i < len; i++) {
    dp[i] = [0, 0, 0]
  }
  // 初始化成本
  dp[0][0] = -prices[0]
  for (let i = 1; i < len; i++) {
    dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][2] - prices[i])
    dp[i][1] = dp[i - 1][0] + prices[i]
    dp[i][2] = Math.max(dp[i - 1][1], dp[i - 1][2])
  }
  return Math.max(dp[len - 1][1], dp[len - 1][2])
}

优化:

  1. 存储降维
  • 从上面的逻辑可以发现当 i 递增时,计算当前 i 的不同状态收益只需要 i-1 不同状态收益
  • 那可以声明一个中间变量来存贮 i-1 的状态就可以替代 dp 的作用了
/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function (prices) {
  let len = prices.length
  // prices小于2是买入来不及卖出
  if (len < 2) return 0
  // 初始化存储对象
  let A = -prices[0],
    B = 0,
    C = 0
  // 初始化成本
  for (let i = 1; i < len; i++) {
    let a = Math.max(A, C - prices[i])
    let b = A + prices[i]
    let c = Math.max(B, C)
    A = a
    B = b
    C = c
  }
  return Math.max(B, C)
}
  1. 状态归纳
  • 0 -> 持有
  • 1 -> 冷冻期
  • 2 -> 不持有

可以归纳为:

  • 0 -> 持有 hold
  • 1 -> 不持有(不持有,冷冻期) unhold

持有:

  • 第一天成本 -prices[0]

  • i=1 第二天持有,有两种可能(前一天不存在持有冷冻情况):

    • 第一天买入:hold[i - 1] 及 -prices[0]
    • 第二天买入:hold[i - 1] -prices[1],-prices[0]-prices[1]
  • i 大于 1 持有时(前一天存在持有冷冻情况):

    • 第 i-1 天持有:hold[i - 1]
    • 第 i-1 天不持有:
      • 为冷冻,则说明第 i-2 天卖出,第 i 天买入:
        第 i-2 天不持有的收益减去第 i 天买入的成本
        unhold[i - 2]-prices[i]
      • 不为冷冻 则最近的一次最大收益应为 hold[i - 2]小于 hold[i - 1]忽略

不持有:

  • 第 n-1 天有两种可能:
    • 持有,今天不持有卖出今天的价格算入收益:hold[i - 1] + prices[i]
    • 不持有,无操作, 其最大收益保持不变
/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function (prices) {
  let len = prices.length
  // prices小于2是买入来不及卖出
  if (len < 2) return 0
  let hold = Array(len),
    unhold = Array(len)
  // 初始化存储对象
  hold[0] = -prices[0]
  unhold[0] = 0
  for (let i = 1; i < len; i++) {
    if (i == 1) {
      hold[i] = Math.max(-prices[0], -prices[1])
    } else {
      hold[i] = Math.max(hold[i - 1], unhold[i - 2] - prices[i], unhold[i - 1])
    }
    unhold[i] = Math.max(unhold[i - 1], hold[i - 1] + prices[i])
  }
  return unhold[len - 1]
}

你可能感兴趣的:(一天一大leet)