代码随想录算法训练营第四十九天 | 123. 买卖股票的最佳时机 III、188. 买卖股票的最佳时机 IV

62 阅读4分钟

123. 买卖股票的最佳时机 III

链接

文章链接

题目链接

第一想法

emmm...刚开始还是想的是按照买卖股票的最佳时机的模版写,唯一的不同就是限制最多买两次,这就很有问题了,只卖一次会写,无限次买也会写,限定次数就不会写了...之后尝试了一下,发现还是有问题...代码如下:

//只是为了记录
想法为记录每次买股票和卖股票的价格,之后从中间选择差值最大的两个就可以了
function maxProfit(prices: number[]): number {
  let dp: number[][] = new Array(prices.length).fill(0).map((item) => new Array())
  let cost: number[] = new Array()
  let buy: number[] = new Array()
  dp[0] = [-prices[0], 0]
  buy[0] = prices[0]
  for (let i = 1; i < prices.length; i++) {
    if (dp[i - 1][0] < dp[i - 1][1] - prices[i]) {//买股票
      buy.push(prices[i])
    }
    if (dp[i - 1][1] < dp[i - 1][0] + prices[i]) {//卖股票
      cost.push(prices[i])
    }
    dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i])
    dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i])
  }
  if (cost.length > 2 && buy.length > 2) {//买股票和卖股票的次数大于2是就计算最大差值的两次
    let sum: number[] = []
    for (let i = 0; i < buy.length; i++) {
      sum[i] = cost[i] - buy[i]
    }
    sum.sort((a, b) => b - a)
    return sum[0] + sum[1]
  }
  return dp[prices.length - 1][1]
}

这个有个问题,就是因为递推公式都是和上一次卖或买股票有关,所以我这种想法写出来有时候buy和cost不是对应的(有时候买了两次,有时候卖了两次)

看完文章后的想法

emmm....果然,我还是没有完全理解这道题的dp数组的含义,这道题就是如果能够深刻理解dp数组的含义,那么这道题就很容易做出来了,接下来递归五部曲:

  1. dp数组的含义,这次还是二维数组,不过dp[i].length不是2了,而是5了,分别的含义为dp[i][0]:不进行操作;dp[i][1]:第一次持有股票;dp[i][2]:第一次不持有股票;dp[i][3]:第二次持有股票;dp[i][4]:第二次不持有股票;d
  2. 先来递推公式,这次递推是非常有规律的:
    dp[i][0] = dp[i - 1][0],因为dp[i][0]是不进行操作,所以一定是0的;
    dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i])第一次持有股票有两种情况,i-1天就持有,或者是i-1天没有持有,今天买入;
    dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + prices[i])第一次不持有有两种情况,第i-1天就没有持有或者第i-1天持有但第i天卖出就不持有了
    dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - prices[i])第二次持有股票有两种情况,i-1天就二次持有,或者是i-1天没有持有,今天买入;
    dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + prices[i])第而次不持有有两种情况,第i-1天就没有持有或者第i-1天持有但第i天卖出就不持有了
  1. 这道题不好理解的也有这里,dp初始化,按照之前的想法那么dp[0][0]=0和dp[0][1]=-prices[0]是比较好理解的,但是dp[0][2]之后就不容易想到了:dp[0][2]=0可以理解为我第一天买入又卖出了,所以手里的现金仍然是0;dp[0][3]=-prices[0]理解为我第一天买入又卖出又买入;dp[0][4]=0理解为我第一天买入又卖出又买入又卖出
  2. 遍历顺序,这道题也是一样的,从前到后
  3. 举例如图:

image.png 代码如下:

function maxProfit(prices: number[]): number {
  let dp: number[][] = new Array(prices.length).fill(0).map((item) => new Array())
  dp[0] = [0, -prices[0], 0, -prices[0], 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]//这里返回第二次不持有后的现金最大值
}

思考

这道题,确实没想到,动态规范的思想果然好丰富,内容也好多,这道题标注了.....

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

链接

文章链接

题目链接

第一想法

这道题其实就是123. 买卖股票的最佳时机 III这道题的一般形式,话不多说,递归五部曲:

  1. dp数组的含义 dp[i][0]还是为不操作,dp[i]2n-1为第n次持有股票时的最大现金;dp[i][2n]为第n次不持有股票时的最大现金;
  2. 递推公式,以前是要一个一个写,这回直接一个for循环就搞定了,代码如下:
    dp[i][0] = dp[i - 1][0]
    for (let j = 1; j < 2 * k + 1; j++) {
      if (j % 2 === 1) dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] - prices[i])
      else dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] + prices[i])
    }
  1. 初始化,还是和以前一样,下标为奇数的设置为-prices[0]
  2. 遍历顺序:从前到后
function maxProfit(k: number, prices: number[]): number {
  let dp: number[][] = new Array(prices.length).fill(0).map((item) => new Array())
  dp[0] = new Array(2 * k + 1).fill(0)
  for (let i = 1; i < dp[0].length; i += 2) {
    dp[0][i] = -prices[0]
  }
  for (let i = 1; i < prices.length; i++) {
    dp[i][0] = dp[i - 1][0]
    for (let j = 1; j < 2 * k + 1; j++) {
      if (j % 2 === 1) dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] - prices[i])//奇数买
      else dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] + prices[i])//偶数买
    }
  }
  return dp[prices.length - 1][2 * k]
}

看完文章的想法

文章的想法和我的是一致的,果然,我的类推的技能还是比较强的,初始发现技能比较弱....这里就不再多说了,想看更详细的代码可以去代码随想录中查看。

思考

这道题就是123. 买卖股票的最佳时机 III他的拓展形式,只要III会了,这道题就很容易就写出来了。

今日总结

今天耗时2.5小时,其中123. 买卖股票的最佳时机 III这道题确实没想到。而188. 买卖股票的最佳时机 IV这道题通过III就很容易递推出来。