Java通过动态规划设计股票买卖最佳时机

 更新时间:2022年10月20日 16:45:56   作者:刘婉晴  
动态规划可谓是大名鼎鼎,笔试面试中的高频考点,也是重点难点,动态规划类型题目灵活多变,难度系数也相对较高,往往我们做不好动态规划的题目就会与心仪的offer失之交臂,本篇文章我们就一起来研究一下动态规划设计股票买卖最佳时机

买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

示例:

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

动态规划

因为对每一天只有两个状态,买入股票和卖出股票,因此定义 dp[][2]

1. dp[i][2] 数组表示的含义 :

dp[i][0] 表示第 i 天买入股票能获得的最大利润,dp[i][1] 表示第一天卖出股票能获得的最大利润

2. 状态转移方程:

dp[i][0] = max(dp[i-1][0], -prices[i])

解释: 第 i 天对买入股票状态,只有两种操作,买入股票或者不买股票。因此,第 i 天能获得的最大利润为 第 i 天不买股票(dp[i-1][0]) 和 第 i 天买入股票(-prices[i]) 的利润的最大值。

dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i])

解释: 第 i 天对卖出股票,只有两种操作,卖出股票或者不卖出股票。因此,第 i 天能获得的最大利润为 第 i 天卖出股票 (dp[i-1][0] + prices[i])和 第 i 天不卖出股票 的利润的最大值 (dp[i-1][1]

3. 初始化

第 0 天买入,得到的利润为 -price[0]

第 0 天卖出,得到的利润为 0

dp[0][0] = -price[0]
dp[0][1] = 0

代码:

    // 买卖股票的最佳时机
    public int maxProfit(int[] prices) {
        // dp 表示当天能获得的最大利润
        int[][] dp = new int[prices.length][2]; // 对每一天来说,就两个状态,一个是买入,一个是卖出
        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];
    }

变式一: 可以重复买入,卖出

   // 买卖股票的最佳时机2 —— 可以多次买入和卖出,但是注意每次只能持有一张股票,就是必须是 买 - 卖 - 买 - 卖 - 买 - 卖 ....这种顺序
    public int maxProfit2(int[] prices) {
        // 本题与上题的区别就在于状态转移方程不同 -- 本题可以用之前得到的利润 (即 dp[i-1][1],上一轮卖出后剩下的钱) 再去买入,而上一题必须 0-price[i] 买入
        // dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1]-prices[i]); ---- 买入股票能得到的最大利润状态转移方程有变化
        int[][] dp = new int[prices.length][2];
        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], dp[i-1][1] - prices[i]); // 当天可能执行股票买入
            dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] + prices[i]); // 当他可能执行股票卖出
        }
        return dp[prices.length-1][1];
    }

变式二:可以执行买入卖出操作两次

   // 买卖股票的最佳时机3 —— 本题与上一题的区别在于,本题只能执行两次买入、卖出操作
    public int maxProfit3(int[] prices) {
        // 每次可执行的操作状态为 ,第一次买入、第一次卖出、第二次买入、第二次卖出,因此 dp 定义为
        int[][] dp = new int[prices.length][4];
        dp[0][0] = -prices[0];
        dp[0][1] = 0;
        dp[0][2] = -prices[0];
        dp[0][3] = 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]);
            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]);
        }
        return dp[prices.length-1][3];
    }

变式三:可以执行买卖操作 K 次

    // 买卖股票的最佳时期 —— 最多进行 K 次交易
    public int maxProfit(int k, int[] prices) {
        // 就是两次交易的升级版
        int[][] dp = new int[prices.length][2*k+1]; // 奇数表示买入,偶数表示卖出
        // 初始化
        for(int j=1; j<2*k; j+=2){
            dp[0][j] = -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];
    }

变式四:含冷冻期

   // 买卖股票含冷冻期
    public int maxProfit(int[] prices) {
        // 一共四种状态 买入 卖出(早就卖出保存卖出态,当天卖出) 冷冻
        int dp[][] = new int[prices.length][4];
        // 初始化
        dp[0][0] = -prices[0];
        for(int i=1; i<prices.length; i++){
            // 买入状态 —— 前一天就是买入状态 + 前一天是卖出状态今天买入了 + 前一天是冷冻状态今天买入了
            dp[i][0] = Math.max(dp[i-1][0], Math.max(dp[i-1][3]-prices[i], dp[i-1][2]-prices[i]) );
            // 卖出态1 —— 前一天就是卖出态1 + 前一天是冷冻态
            dp[i][1] = Math.max(dp[i-1][1], dp[i-1][3]);
            // 卖出态2 —— 当天卖出了
            dp[i][2] = dp[i-1][0] + prices[i];
            // 冷冻态 —— 前一天是卖出态2
            dp[i][3] = dp[i-1][2];
        }
        return Math.max(dp[prices.length-1][1], Math.max(dp[prices.length-1][2], dp[prices.length-1][3]));
    }

变式五:含手续费

    // 买卖股票的最佳时机,含手续费
    public int maxProfit2(int[] prices, int fee) {
        int[][] dp = new int[prices.length][2];
        // 初始化
        dp[0][0] = -prices[0] - fee;
        for(int i=1; i<prices.length; i++){
            dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] - prices[i] - fee);
            dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] + prices[i]);
        }
        return dp[prices.length-1][1];
    }

到此这篇关于Java通过动态规划设计股票买卖最佳时机的文章就介绍到这了,更多相关Java股票买卖内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • MyBatis Plus Mapper CRUD接口测试方式

    MyBatis Plus Mapper CRUD接口测试方式

    在数据库管理系统中,插入记录是添加新数据条目,而删除操作包括根据主键ID单条删除和批量删除,也可以基于特定条件进行删除,删除操作的SQL语句是通过键值对在Map中拼接而成,如delete from 表 where key1=value1 AND key2=value2
    2024-09-09
  • 如何解决@value获取不到yaml数组的问题

    如何解决@value获取不到yaml数组的问题

    文章介绍了在使用YAML配置文件时,通过@Value注解获取整数和数组列表的配置方法,并提供了两种解决方案:一种适用于非嵌套列表,另一种适用于嵌套列表等复杂配置
    2024-11-11
  • Spring框架中@PostConstruct注解详解

    Spring框架中@PostConstruct注解详解

    在Spring项目经常遇到@PostConstruct注解,下面这篇文章主要给大家介绍了关于Spring框架中@PostConstruct注解的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-07-07
  • 解决@FeignClient注入service失败问题

    解决@FeignClient注入service失败问题

    这篇文章主要介绍了解决@FeignClient注入service失败问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • Java Kafka实现延迟队列的示例代码

    Java Kafka实现延迟队列的示例代码

    kafka作为一个使用广泛的消息队列,很多人都不会陌生。本文将利用Kafka实现延迟队列,文中的示例代码讲解详细,感兴趣的小伙伴可以尝试一下
    2022-08-08
  • 浅谈java随机数的陷阱

    浅谈java随机数的陷阱

    这篇文章主要介绍了浅谈java随机数的陷阱,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-09-09
  • 使用springboot制作博客管理系统

    使用springboot制作博客管理系统

    这篇文章主要介绍了使用springboot制作博客管理系统,文中有非常详细的代码示例,对正在学习springboot的小伙伴有很大的帮助,感兴趣的小伙伴可以参考一下
    2021-08-08
  • SpringBoot整合Xxl-job实现定时任务的全过程

    SpringBoot整合Xxl-job实现定时任务的全过程

    XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展,下面这篇文章主要给大家介绍了关于SpringBoot整合Xxl-job实现定时任务的相关资料,需要的朋友可以参考下
    2022-01-01
  • springboot项目中配置redis详细的教程

    springboot项目中配置redis详细的教程

    Redis是一种高性能的键值存储数据库,而Spring Boot是一个简化了开发过程的Java框架,这篇文章主要给大家介绍了关于springboot项目中配置redis详细的教程,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-04-04
  • SpringBoot+Redis+Lua防止IP重复防刷攻击的方法

    SpringBoot+Redis+Lua防止IP重复防刷攻击的方法

    本文主要介绍了SpringBoot+Redis+Lua防止IP重复防刷攻击的方法,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12

最新评论