第九章 动态规划 part11 123. 买卖股票的最佳时机III 188. 买卖股票的最佳时机IV

第五十天| 第九章 动态规划 part11 123. 买卖股票的最佳时机III 188. 买卖股票的最佳时机IV

一、123. 买卖股票的最佳时机III==(难难难难难)==

  • 题目链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/

  • 题目介绍:

    • 给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

      设计一个算法来计算你所能获取的最大利润。你最多可以完成两笔交易

      **注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

      示例 1:

      输入:prices = [3,3,5,0,0,3,1,4]
      输出:6
      解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
           随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。
      
  • 思路:

    • 注意:本题是最多买卖两次股票

    • DP五部曲:

      • (1)确定dp数组及下标含义:

        • 一天一共就有五个状态,

          1. 没有操作 (其实我们也可以不设置这个状态)
          2. 第一次持有股票
          3. 第一次不持有股票
          4. 第二次持有股票
          5. 第二次不持有股票
        • dp[i][j]中 i表示第i天,j为 [0 - 4] 五个状态,dp[i][j]表示第i天状态j所剩最大现金。
          
        • 五个状态分别如下:

          • dp[i][0]: 表示第i天,不操作股票的时候,手中的最大金额(也就是0)
            dp[i][1]: 表示第i天,第一次持有该股票,手中的最大金额
            dp[i][2]: 表示第i天,第一次不持有该股票,手中的最大金额
            dp[i][3]: 表示第i天,第二次持有该股票,手中的最大金额
            dp[i][4]: 表示第i天,第二次不持有该股票,手中最大的金额
            
      • (2)确定递推公式:

        • 和I、II一样的思路

        • 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]);
          
      • (3)初始化dp数组:

        • dp[0][0] = 0;
          dp[0][1] = -prices[0];
          dp[0][2] = 0;
          dp[0][3] = -prices[0];
          dp[0][4] = 0;
          
        • 分别表示的含义是:
          (1)dp[0][0]:第0天,没有任何操作手里的金额是0
          (2)dp[0][1]:第0天,第一次持有股票,手里的金额就是买第0天的股票花掉的
          (3)dp[0][2]:第0天,第一次不持有股票,手里的金额就是买完再卖了第0天的股票,也就是0
          (4)dp[0][3]:为什么会出现第二次持有呢?可以当作第一天买了又卖了,然后再买
          (5)dp[0][4]:同理。
          
      • (4)确定遍历顺序:

        • 正序
      • (5)打印dp数组:

        • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  • 代码:

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0) return 0;
        // (1)确定dp数组及下标含义
        // dp[i][0]: 表示第i天,不操作股票的时候,手中的最大金额(也就是0)
        // dp[i][1]: 表示第i天,第一次持有该股票,手中的最大金额
        // dp[i][2]: 表示第i天,第一次不持有该股票,手中的最大金额
        // dp[i][3]: 表示第i天,第二次持有该股票,手中的最大金额
        // dp[i][4]: 表示第i天,第二次不持有该股票,手中最大的金额
        int[][] dp = new int[prices.length][5];
        // (3)初始化dp数组
        dp[0][1] = -prices[0];
        dp[0][3] = -prices[0];
        // (4)确定遍历顺序
        for (int i = 1; i < prices.length; i++) {
            // (2)确定递推公式
            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];
    }
}
  • 注意:
    • 这里的返回值再强调一下:
      • 首先众所周知,你不持有股票手里的金额一定是大于持有股票的,所以返回的状态一定是偶数状态。
      • 那为什么不返回状态2呢?
        • 是因为状态4其实是包含了状态2的,因为如果状态2达到最大值,状态4一定会保持这个最大值的。因此直接返回状态4即可。

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

  • 题目链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iv/

  • 题目介绍:

    • 给你一个整数数组 prices 和一个整数 k ,其中 prices[i] 是某支给定的股票在第 i 天的价格。

      设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。也就是说,你最多可以买 k 次,卖 k 次。

      **注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

      示例 1:

      输入:k = 2, prices = [2,4,1]
      输出:2
      解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。
      
  • 思路:

    • 注意:本题是最多买卖k次股票
    • 根据上道题可以推算出本题,只需要把握一些关键的边界点即可
      • (1)dp数组的初始化长度:2k+1
      • (2)初始化dp数组:奇数状态初始为-prices[0],边界是< 2 * k
      • (3)递推公式:需要一个j表示状态,j从0开始,每次循环+2,因此到最后一位2k时,j=2k-2。因此j的边界是:j < 2k - 1;
  • 代码:

class Solution {
    public int maxProfit(int k, int[] prices) {
        if (prices == null || prices.length == 0) return 0;
        int[][] dp = new int[prices.length][2*k+1];
        for (int i = 1; i < 2*k; i += 2) {
            dp[0][i] = -prices[0];
        }
        for (int i = 1; i < prices.length; i++) {
            for (int j = 0; j < 2*k-1; 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];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值