• 力扣记录:动态规划4股票问题——121,122,123,188 ,309,714买卖股票的最佳时机(I,II,III,IV,含冷冻期,含手续费)


    本次题目

    • 121 买卖股票的最佳时机(只能买卖一次)
    • 122 买卖股票的最佳时机II(可以买卖多次)
    • 123 买卖股票的最佳时机III(最多买卖两次)
    • 188 买卖股票的最佳时机IV(最多买卖k次)
    • 309 买卖股票的最佳时机含冷冻期(买卖多次,卖出有一天冷冻期)
    • 714 买卖股票的最佳时机含手续费(买卖多次,每次有手续费)

    121 买卖股票的最佳时机

    • 贪心:取左边最小值,右边最大值,得到差值最大。
    class Solution {
        public int maxProfit(int[] prices) {
            //贪心
            int low = Integer.MAX_VALUE;
            int result = 0;
            for(int i = 0; i < prices.length; i++){
                low = Math.min(low, prices[i]);
                result = Math.max(result, prices[i] - low);
            }
            //返回结果
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • DP:
      1. 定义dp数组dp[i][0]表示第i天持有(不一定是当天买入)股票得到的最大利润,dp[i][1]表示表示第i天不持有股票得到的最大利润;
      2. 递推公式dp[i][0] = max(dp[i - 1][0], -prices[i]),dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);
      3. 初始化dp[0][0] = -prices[0],dp[0][1] = 0;
      4. 遍历顺序正序;
      5. 举例。
    • 补充:可以优化为滚动数组。
    class Solution {
        public int maxProfit(int[] prices) {
            //DP
            //判断特殊情况
            if(prices.length == 1) return 0;
            //定义dp数组dp[i][0]表示第i天持有(不一定是当天买入)股票得到的最大利润
            //dp[i][1]表示表示第i天不持有股票得到的最大利润
            int[][] dp = new int[prices.length][2];
            //初始化dp[0][0] = -prices[0],dp[0][1] = 0
            dp[0][0] = -prices[0];
            dp[0][1] = 0;
            //遍历顺序正序
            for(int i = 1; i < prices.length; i++){
                dp[i][0] = Math.max(dp[i - 1][0], -prices[i]);
                dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
            }
            //返回结果
            return dp[prices.length - 1][1];
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 优化:
    class Solution {
        public int maxProfit(int[] prices) {
            //DP
            //判断特殊情况
            if(prices.length == 1) return 0;
            //定义dp数组dp[i][0]表示第i天持有(不一定是当天买入)股票得到的最大利润
            //dp[i][1]表示表示第i天不持有股票得到的最大利润
            //存两天状态即可
            int[][] dp = new int[2][2];
            //初始化dp[0][0] = -prices[0],dp[0][1] = 0
            dp[0][0] = -prices[0];
            dp[0][1] = 0;
            //遍历顺序正序
            for(int i = 1; i < prices.length; i++){
                dp[1][0] = Math.max(dp[0][0], -prices[i]);
                dp[1][1] = Math.max(dp[0][1], dp[0][0] + prices[i]);
                dp[0][0] = dp[1][0];
                dp[0][1] = dp[1][1];
            }
            //返回结果
            return dp[1][1];
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    122 买卖股票的最佳时机II

    • 贪心之前实现过
    • DP:同上121 买卖股票的最佳时机,但是可以买卖多次,因此持有股票的递推公式修改为dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i])。同样可优化为滚动数组。
    • DP优化:
    class Solution {
        public int maxProfit(int[] prices) {
            //DP
            //判断特殊情况
            if(prices.length == 1) return 0;
            //定义dp数组dp[i][0]表示第i天持有(不一定是当天买入)股票得到的最大利润
            //dp[i][1]表示第i天不持有股票得到的最大利润
            //存两天状态即可
            int[][] dp = new int[2][2];
            //初始化dp[0][0] = -prices[0],dp[0][1] = 0
            dp[0][0] = -prices[0];
            dp[0][1] = 0;
            //遍历顺序正序
            for(int i = 1; i < prices.length; i++){
                dp[1][0] = Math.max(dp[0][0], dp[0][1] - prices[i]);    //上一次买卖的利润
                dp[1][1] = Math.max(dp[0][1], dp[0][0] + prices[i]);
                dp[0][0] = dp[1][0];
                dp[0][1] = dp[1][1];
            }
            //返回结果
            return dp[1][1];
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    123 买卖股票的最佳时机III

    • 最多买卖两次,因此当天可能有五种状态(不买卖,第一次买,第一次卖,第二次买,第二次卖)。
    • DP:
      1. 定义dp数组dp[i][j]表示第i天状态j得到的最大利润;
      2. 递推公式dp[i][1] = max(dp[i-1][0] - prices[i], dp[i - 1][1]),dp[i][2] = max(dp[i - 1][1] + prices[i], dp[i - 1][2]),dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i]),dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
      3. 初始化dp[0][0] = 0,dp[0][1] = -prices[0],dp[0][2] = 0,dp[0][3] = -prices[0],dp[0][4] = 0;
      4. 遍历顺序正序;
      5. 举例。
    • 补充:可以使用滚动数组优化。
    class Solution {
        public int maxProfit(int[] prices) {
            //DP
            //定义dp数组dp[i][j]表示第i天状态j得到的最大利润
            //最多买卖两次,因此当天可能有五种状态(不买卖,第一次买,第一次卖,第二次买,第二次卖)
            int[][] dp = new int[prices.length][5];
            //初始化
            dp[0][0] = 0;
            dp[0][1] = -prices[0];
            dp[0][2] = 0;
            dp[0][3] = -prices[0];
            dp[0][4] = 0;
            //遍历顺序正序
            for(int i = 1; i < prices.length; i++){
                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];
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 优化:
    class Solution {
        public int maxProfit(int[] prices) {
            //DP
            //定义dp数组dp[i][j]表示第i天状态j得到的最大利润
            //最多买卖两次,因此当天可能有五种状态(不买卖,第一次买,第一次卖,第二次买,第二次卖)
            //滚动数组优化,记录两天即可
            int[][] dp = new int[2][5];
            //初始化
            dp[0][0] = 0;
            dp[0][1] = -prices[0];
            dp[0][2] = 0;
            dp[0][3] = -prices[0];
            dp[0][4] = 0;
            //遍历顺序正序
            for(int i = 1; i < prices.length; i++){
                dp[1][1] = Math.max(dp[0][1], dp[0][0] - prices[i]);
                dp[1][2] = Math.max(dp[0][2], dp[0][1] + prices[i]);
                dp[1][3] = Math.max(dp[0][3], dp[0][2] - prices[i]);
                dp[1][4] = Math.max(dp[0][4], dp[0][3] + prices[i]);
                dp[0][1] = dp[1][1];
                dp[0][2] = dp[1][2];
                dp[0][3] = dp[1][3];
                dp[0][4] = dp[1][4];
            }
            //返回结果
            return dp[1][4];
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    188 买卖股票的最佳时机IV

    • 同上123 买卖股票的最佳时机III,状态为2k+1种(奇数为买入,偶数为卖出)。
    • DP:
      1. 定义dp数组dp[i][j]表示第i天状态j得到的最大利润;
      2. 递推公式dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]),dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);
      3. 初始化奇数为-prices[0],偶数为0;
      4. 遍历顺序正序;
      5. 举例。
    • 补充:可以使用滚动数组优化。
    • DP优化:
    class Solution {
        public int maxProfit(int k, int[] prices) {
            //DP
            //判断特殊情况
            if(prices.length <= 1) return 0;
            //定义dp数组dp[i][j]表示第i天状态j得到的最大利润
            //最多买卖k次,因此当天可能有2k + 1种状态
            //滚动数组优化,记录两天即可
            int[][] dp = new int[2][2 * k + 1];
            //初始化奇数为-prices[0],偶数为0
            for(int i = 1; i < 2 * k + 1; 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[1][j + 1] = Math.max(dp[0][j + 1], dp[0][j] - prices[i]);
                    dp[1][j + 2] = Math.max(dp[0][j + 2], dp[0][j + 1] + prices[i]);
                    dp[0][j + 1] = dp[1][j + 1];
                    dp[0][j + 2] = dp[1][j + 2];
                }
            }
            //返回结果
            return dp[1][2 * k];
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    309 买卖股票的最佳时机含冷冻期

    • 对比上面的122 买卖股票的最佳时机II,同样可以买卖多次,但是卖出后有一天冷冻期不能买入,因此当天有四种状态(买入,两天前卖出,当天卖出,冷冻期)。
    • DP:
      1. 定义dp数组dp[i][j]表示第i天状态j得到的最大利润;
      2. 递推公式dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][3], dp[i - 1][1]) - prices[i]),dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]),dp[i][2] = dp[i - 1][0] + prices[i],dp[i][3] = dp[i - 1][2];
      3. 初始化dp[0][0] = -prices[0],dp[0][1] = 0,dp[0][2] = 0,dp[0][3] = 0,最后结果是取状态二、状态三和状态四的最大值;
      4. 遍历顺序正序;
      5. 举例。
    • 补充:可以使用滚动数组优化。
    • DP优化:
    class Solution {
        public int maxProfit(int[] prices) {
            //DP
            //判断特殊情况
            if(prices.length == 1) return 0;
            //定义dp数组dp[i][1]表示第i天状态j得到的最大利润
            //当天有四种状态(买入,两天前卖出,当天卖出,冷冻期)
            //存两天状态即可
            int[][] dp = new int[2][4];
            //初始化
            dp[0][0] = -prices[0];
            dp[0][1] = 0;
            dp[0][2] = 0;
            dp[0][3] = 0;
            //遍历顺序正序
            for(int i = 1; i < prices.length; i++){
                dp[1][0] = Math.max(dp[0][0], Math.max(dp[0][1] - prices[i], dp[0][3] - prices[i]));
                dp[1][1] = Math.max(dp[0][1], dp[0][3]);
                dp[1][2] = dp[0][0] + prices[i];
                dp[1][3] = dp[0][2];
                dp[0][0] = dp[1][0];
                dp[0][1] = dp[1][1];
                dp[0][2] = dp[1][2];
                dp[0][3] = dp[1][3];
            }
            //返回结果
            //最后结果是取状态二、状态三和状态四的最大值
            return Math.max(dp[1][1], Math.max(dp[1][2], dp[1][3]));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    714 买卖股票的最佳时机含手续费

    • 贪心之前实现过
    • 对比上面的122 买卖股票的最佳时机II,同样可以买卖多次,但是每次买卖都需要手续费,因此计算利润时需要扣除手续费,卖出股票时递推公式修改为dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee)。
    • DP优化:
    class Solution {
        public int maxProfit(int[] prices, int fee) {
            //DP
            //判断特殊情况
            if(prices.length == 1) return 0;
            //定义dp数组dp[i][0]表示第i天持有(不一定是当天买入)股票得到的最大利润
            //dp[i][1]表示第i天不持有股票得到的最大利润
            //存两天状态即可
            int[][] dp = new int[2][2];
            //初始化
            dp[0][0] = -prices[0];
            dp[0][1] = 0;
            //遍历顺序正序
            for(int i = 1; i < prices.length; i++){
                dp[1][0] = Math.max(dp[0][0], dp[0][1] - prices[i]);    //上一次买卖的利润
                dp[1][1] = Math.max(dp[0][1], dp[0][0] + prices[i] - fee);    //计算利润扣除手续费
                dp[0][0] = dp[1][0];
                dp[0][1] = dp[1][1];
            }
            //返回结果
            return dp[1][1];
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
  • 相关阅读:
    【Java进阶篇】第五章 集合(下)--Map集合
    卡码网语言基础课 |句子缩写
    【无标题】
    LambdaQueryWrapper 自定义返回字段(分组后统计)
    JAVA基础——【笔记】14.集合
    有哪些好用的数据填报系统推荐?_光点科技
    史上最全架构师知识图谱(纯干货)
    基于java web个人财务管理系统
    互联网摸鱼日报(2022-11-07)
    Linux操作(查询日志)
  • 原文地址:https://blog.csdn.net/Kiwi_fruit/article/details/124591122