一篇文章带你用动态规划解决股票购买时机问题

news2024/11/22 10:37:04

动态规划的解题步骤可以分为以下五步,大家先好好记住

1.创建dp数组以及明确dp数组下标的含义  2.制定递推公式  3.初始化  4.遍历顺序  5.验证结果


 股票购买时机问题的解题核心思路

当天的收益是根据前一天持有股票还是不持有股票的状态决定的

那么很自然的我们就想到了使用动态规划的思想来解决问题,接下来就根据动态规划以及解题的核心思想来解决 股票购买时机问题 


121. 买卖股票的最佳时机

根据题意: 某一天 买入这只股票,并选择在 未来的某一个不同的日子 

也就是说我们只用买卖股票一次即可 那么根据动态规划的解题步骤以及核心思想我们一步步分析

1.创建dp数组以及明确dp数组下标的含义

//0 表示持不有股票的状态 1表示持有股票的状态
int[][] dp = new int[prices.length][2];

2.制定递推公式

//持有的状态 - 要么是保持持有状态 要么是今天买入股票
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]);

3.初始化

dp[0][0] = -prices[0];
dp[0][1] = 0;

4.遍历顺序

//从前往后遍历- 由于我们是从 i = 1 开始遍历的所以是prices[i-1]
for(int i = 1;i < dp.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]);
}

5.验证

 

最后贴上完整代码

    public int maxProfit(int[] prices) {
        int len = prices.length;
        //dp数组下标的含义是当日持有股票和不持有股票的最大利润
        int[][] dp = new int[len][2];
        //递推公式
        //dp[i][0] 表示持有股票 dp[i][1] 表示不持有股票
        //dp[i][0] = Math.max(dp[i-1][0],dp[i-1][0] - prices[i]);
        //dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0] + prices[i]);
        //初始化
        dp[0][0] = -prices[0];
        dp[0][1] = 0;

        for(int i = 1;i < dp.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 Math.max(dp[len-1][0],dp[len-1][1]);
    }

122. 买卖股票的最佳时机 II

本题遇上一题最大的不同就是可以多次买卖股票了,大题思路和上一题是差不多的

所以我们只需要在 2.指定递推公式 这里修改一下即可

由于可以多次买卖股票了,那么在今天买入股票这个步骤就需要参考昨天不持有股票的状态了

这就是和上一题不同的地方,上一题由于只需要买入一次,所以不用参考任何状态

那么只需要简单的进行修改即可


        for(int i = 1;i < dp.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]);
        }

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

本题说了只能购买两次股票那么难度就增加了,但是核心思路“当天的收益是根据是前一天持有股票还是不持有股票的状态决定的”还是不变的

1.创建dp数组以及明确dp数组下标的含义 

int len = prices.length;
//dp数组的下标的含义是当日持有股票或者不持有股票获得的最大收益 
//0~1表示第一次持有股票 2~3表示第二次持有股票
int[][] dp = new int[len + 1][4];

2.制定递推公式 

第一次持有是第一题的递推公式思路(只能买卖股票一次)

第二次持有是第二题的思路,因为此时买卖股票需要结合上一次买卖股票的状态来决定(多次买卖股票)

结合上两题的思想我们得到递推公式

//第一次持有
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]);

3.初始化 

//第一次持有
dp[0][0] = -prices[0];
//第一次不持有
dp[0][1] = 0;
//第二次持有
dp[0][2] = -prices[0];
//第二次不持有
dp[0][3] = 0;

4.遍历顺序 

从前往后

        
for(int i = 1;i < dp.length;i++){
     dp[i][0] = Math.max(dp[i-1][0], -prices[i-1]);
     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]);
}

最后完整代码如下

    public int maxProfit(int[] prices) {
        int len = prices.length;
        int[][] dp = new int[len][4];
        //第一次持有
        dp[0][0] = -prices[0];
        //第一次不持有
        dp[0][1] = 0;
        //第二次持有
        dp[0][2] = -prices[0];
        //第二次不持有
        dp[0][3] = 0;

        for(int i = 1;i < dp.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[len-1][3];
    }

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

别被吓着!这题实际上和上一题是一模一样的! 

区别就在于这一题是购买K次,而K则是题目随机分配的

但思路还是一样的!

那么怎么来解决掉0~K次的各个状态呢?答案也很简单!使用for循环

比如初始化的时候我们可以使用for循环来进行初始化

        //初始化
        for(int i = 1;i < k*2 + 1;i += 2){
            //规定奇数是持有的状态 偶数是不持有的状态
            dp[0][i] = -prices[0];
        }

 当我们制定递推公式的时候也是使用for循环来帮助我们实现的

        //遍历顺序
        for(int i = 1;i < len;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]);
            }
        }

你们看完可能会觉得很疑惑:不是说第一次持有股票不需要参考任何状态吗?那这个代码怎么解释

dp[i - 1][j] - prices[i];

 注意我们在初始化的时候是从 i = 1 开始的 也就是说 第一次持有股票时的状态还是

dp[i][0] = - prices[i];

这样大家应该明白了吧

最后贴上完整的代码

class Solution {
    public int maxProfit(int k, int[] prices) {
        int len = prices.length;
        //dp数组
        int[][] dp = new int[len][k*2 + 1];
        //递推公式
        
        //持有操作 奇数
        //dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j] - price[i]);
        //不持有操作 偶数
        //dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j] + price[i]);

        //初始化
        for(int i = 1;i < k*2 + 1;i += 2){
            dp[0][i] = -prices[0];
        }
        //遍历顺序
        for(int i = 1;i < len;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[len - 1][k*2];
    }
}

309. 买卖股票的最佳时机含冷冻期(老实说,这题是我觉得股票买卖时机里面最难的一题...)

 想要解决这道难题首先我们得分析好状态,不然会越来越懵逼的

首先由持有状态 不持有状态(保持不持有状态,前一天是冷冻期状态)  冷冻期状态 

我们先定义好dp数组

        int[][] dp = new int[len][4];
        //1.持有股票(保持持有股票/今天买入股票) 2.保持不持有股票 3.今天卖出股票 4.处于冷冻期
        //持有
        dp[0][0] = -prices[0];
        //不持有
        dp[0][1] = 0;
        //今天卖出股票
        dp[0][2] = 0;
        //处于冷冻期 = 昨天卖出股票
        dp[0][3] = dp[0][2];

持有状态可以由不持有状态和冷冻期状态进行推导,它可以是保持持有状态也可以是今天买入

dp[i][0] = Math.max(Math.max(dp[i-1][1],dp[i-1][3]) - prices[i],dp[i-1][0]);

那么不持有状态可以由保持不持有状态和前一天是冷冻期进行推导

dp[i][1] = Math.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];

状态推导完了剩下也就简单了,直接上代码

class Solution {
    public int maxProfit(int[] prices) {
        int len = prices.length;
        int[][] dp = new int[len][4];
        //1.持有股票(保持持有股票/今天买入股票) 2.保持不持有股票 3.今天卖出股票 4.处于冷冻期
        //持有
        dp[0][0] = -prices[0];
        //不持有
        dp[0][1] = 0;
        //今天卖出股票
        dp[0][2] = 0;
        //处于冷冻期 = 昨天卖出股票
        dp[0][3] = dp[0][2];

        for(int i = 1;i < len;i++){
            //持有股票
            dp[i][0] = Math.max(Math.max(dp[i-1][1],dp[i-1][3]) - prices[i],dp[i-1][0]);
            //保持不持有股票
            dp[i][1] = Math.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];
        }

        return Math.max(dp[len-1][1],Math.max(dp[len-1][2],dp[len-1][3]));
    }   
}

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

本题实际上只用在第二题的基础上扣除手续费就好了只需要修改这一段代码即可

dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0] + prices[i] - fee);

股票买卖时机问题我们只需要把握一点就是把 持有和不持有 两个状态分析好,题目就会变得很简单

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1092403.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Dijkstra求最短路(图解)

你好&#xff0c;我是Hasity。 今天分享的内容&#xff1a;Dijkstra求最短路这个题目 Dijkstra求最短路I 题目描述 给定一个 n个点 m 条边的有向图&#xff0c;图中可能存在重边和自环&#xff0c;所有边权均为正值。 请你求出 1 号点到 n号点的最短距离&#xff0c;如果无…

关于导入Maven工程项目,更新pom.xml文件仍然爆红的原因

问题描述&#xff1a; 在学习maven工程的时候&#xff0c;把从网上学习的工程导入到IDEA&#xff0c;发现&#xff0c;无论怎么更新&#xff0c;pom.xml文件一直报错&#xff0c;查看settings设置和project Structure仍然没找出问题来。 settings设置如下&#xff1a; 解决问…

短视频如何批量添加水印

在当今的数字时代&#xff0c;短视频已经成为一种非常流行的内容形式。无论是社交媒体还是视频分享网站&#xff0c;短视频都已经成为了一种非常有吸引力的内容。然而&#xff0c;对于一些拥有大量视频内容的创作者来说&#xff0c;添加水印可能是一项繁琐的任务。本文将介绍如…

【windows下docker安装rocketMQ】

namesrv和broker安装就不说了&#xff0c;见如下博客 https://blog.csdn.net/Wonderful1025/article/details/107244434/ 安装rocketMQ-console docker run -d -e "JAVA_OPTS-Drocketmq.config.namesrvAddr192.168.65.2:9876 -Drocketmq.config.isVIPChannelfalse"…

__builtin_return_address()函数的使用方法

__builtin_return_address(0) 是GCC编译器提供的内置函数&#xff0c;用于获取当前函数调用栈中的指定帧&#xff08;frame&#xff09;的返回地址。这个函数通常用于调试和性能分析&#xff0c;以了解程序中的函数调用关系。 下面是关于 __builtin_return_address(0) 函数的一…

SystemC入门学习-第5章 同步逻辑建模

本章重点学习同步逻辑中的触发器&#xff0c;锁存器的一些建模规范&#xff1a; 触发器建模带异步置位/复位带同步置位/复位锁存器建模 5.1 触发器建模 触发器建模的关键是敏感列表的规范。SC_MODULE的规范写法中出现过sensitive 参数列表是事件敏感&#xff0c; 对触发器建模…

操作系统学习笔记4-死锁问题

文章目录 1、死锁逻辑图2、死锁三胞胎3、死锁的原因及必要条件4、死锁处理策略之死锁预防5、死锁处理策略之死锁避免&#xff08;银行家算法&#xff09;6、死锁处理策略之死锁检测与解除 1、死锁逻辑图 2、死锁三胞胎 3、死锁的原因及必要条件 4、死锁处理策略之死锁预防 5、死…

查找组成一个偶数最接近的两个素数

一、题目 二、代码 #include <iostream> using namespace std; bool isPrime(int num)//判断素数 {if (num < 1)return false;if (num 2)return true;if (num % 2 0)return false;for (int i 3; i < num; i){if (num % i 0){return false;}}return true; } in…

win11下的VS2022+QT6+VTK9.2+PCL1.13.1联合开发环境配置及踩坑记录

准备工作&#xff1a; 安装VS2022&#xff1a;这个比较简单&#xff0c;网上随便找个教程就行 安装QT并为VS2022添加QT Creater插件&#xff1a;VS2022配置Qt6_vs2022 qt6-CSDN博客 安装PCL&#xff1a;vs2022配置pcl1.13.1_pcl配置-CSDN博客 安装PCL过程中本身也会安装VTK&…

小程序入门及案例展示

目录 一、小程序简介 1.1 为什么要使用小程序 1.2 小程序可以干什么 二、前期准备 2.1 申请账号 2.2 开发工具下载与安装 三、电商案例演示 四、入门案例 4.1 项目结构解析 4.2 基础操作及语法 4.3 模拟器 4.4 案例演示 4.4.1 新建页面 4.4.2 头部样式设置 4.4.…

Marin说PCB之CoilcraftBourns POC 电感的性能对比

十一小长假本来是一件美好事情。可是天有不测风云&#xff0c;小编我却有祸兮来了。本来是公司的硬件同事强哥要回以色列了&#xff0c;最近他们国家那边都在打仗&#xff0c;强哥本着舍身为国的精神回国抗战去了。小编我就想着在他回国之前搞了篮球比赛送别一下他呢&#xff0…

小程序入门——详细教程

&#x1f3ac; 艳艳耶✌️&#xff1a;个人主页 &#x1f525; 个人专栏 &#xff1a;《Spring与Mybatis集成整合》《Vue.js使用》 ⛺️ 生活的理想&#xff0c;为了不断更新自己 ! 1.微信小程序 入门 1.1什么是小程序&#xff1f; 2017年度百度百科十大热词之一 微信小程…

BUUCTF pwn1_sctf_2016 1

代码分析 查看文件信息然后进行反汇编 关键信息 32位栈不可执行 IDA反汇编 说实话&#xff0c;这个应该是C编写的程序&#xff0c;C基础还是不行&#xff0c;我硬是没看懂这个代码 我查了一下字符串 这里的get_flag是函数&#xff0c;另一个应该就是执行的一个命令了 到IDA…

【LeetCode刷题(数据结构)】:翻转二叉树

方法一&#xff1a;递归 思路与算法 这是一道很经典的二叉树问题。显然&#xff0c;我们从根节点开始&#xff0c;递归地对树进行遍历&#xff0c;并从叶子节点先开始翻转。如果当前遍历到的节点 root\textit{root}root 的左右两棵子树都已经翻转&#xff0c;那么我们只需要交…

【入门】.Net Core 6 WebApi 项目搭建

一、创建项目 1.1.创建新项目&#xff1a;打开开发工具>创建新项目>搜索API>选择C#语言的ASP.NET Core Web API 1.2.配置新项目&#xff1a;**自定义项目信息以及存储路径 1.3.其他信息&#xff1a;这里框架必须选择.NET 6.0,其他配置默认勾选即可&#xff0c;也可以根…

SystemVerilog Assertions应用指南 第一章(1.24章节 “or”运算符)

二进制运算符“or”可以用来逻辑地组合两个序列。只要其中一个序列成功,整个属性就成功。序列s29a和s29b是两个独立的序列。属性p29将两者用“or运算符组合起来。当其中任一序列成功时,属性就成功。 sequence s29a;(posedge clk) a##[1:2] b; endsequencesequence s29b;(posed…

Hadoop3教程(六):HDFS中的DataNode

文章目录 &#xff08;63&#xff09;DataNode工作机制&#xff08;64&#xff09;数据完整性&#xff08;65&#xff09;掉线时限参数设置参考文献 &#xff08;63&#xff09;DataNode工作机制 DataNode内部存储了一个又一个Block&#xff0c;每个block由数据和数据元数据组…

2024年计算机专业Java选题推荐✅(最新、最全、最容易通过的选择)

文章目录 前言选题和具体实现详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&#x…

USDR脱锚事件:稳定币碰上房地产,双重buff想不崩都难!

10月11日&#xff0c;一种名为Real USD&#xff08;USDR&#xff09;的稳定币脱锚&#xff0c;在几个小时内迅速从1美元跌至0.5美元&#xff0c;而这无疑是一场典型的挤兑&#xff0c;主要由该稳定币的高流动性债务与其低流动性抵押物之间存在期限错配所致。 USDR是一种流通量为…

链表oj (7.29)

203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09; 思路1&#xff1a;使用结构体指针 cur 遍历链表&#xff0c;遇到值为 val 时删除&#xff0c;删除之前需要判断是头删还是正常的删除&#xff0c;头删需要改变头指针&#xff1b; 正常的删除需要 cur(待删除节点&am…