动态规划:万变不离其宗,带你吃透股票系列问题

news2024/10/6 5:59:09

前言:

对于买卖股票问题而言,最关键的是我们对问题的处理方式(对于每一天而言,我们应该描述当天买入卖出还是只描述每天股票的只有或者不持有的状态呢?)我们应该描述每天股票是否持有的状态,因为每天持有股票的状态很好描述,只有持有和不持有这两种状态, 但是如果选择描述在哪一天买入卖出类似这种状态,描述起来就很复杂了

一.买卖股票的最佳时机I(只能买一次)

题目描述(链接:力扣)

解题思路

我们使用动规五部曲对其进行分析:

1.数组下标及含义:在这里我们使用二维数组作为dp数组,为什么要使用二维数组呢?因为哪一天作为一个变量,每一天同时也会有两种状态:持有和不持有股票,因此我们使用dp[n][2]作为dp数组,n代表哪一天,2代表两种不同的状态,dp[][]代表当天某种状态下的最大利润。

2.递推公式:对于当前问题而言,只能买卖一次,对于每一天的状态而言dp[n][0]代表着持有股票:如果持有股票,则存在两种可能性:在前一天就持有,或者在当天买入,我们取其中利润的最大值,所以:dp[n][0]=max(dp[n-1][0],-prices[i]])(由于只能买卖一次,如果当天买入,意味着之前肯定没有买入,所以今天买入所获得的利润为-prices[i]),dp[n][1]代表着当天不持有股票的状态,同样存在两种组成的可能性:一种是由dp[n-1][0]继承而来,即前一天的状态就是未持有,今天的状态是前一天状态的延续,第二种是dp[n-1][0]+prices[i],即昨天是持有股票的状态,而今天把股票出售了,同样是两者取最大值。

3.初始化:我们由递推公式能得出的结论是:我们后一天的状态依赖于前一天的状态,所以第一天dp数组的正确性十分重要,dp[0][0]:代表第一天持有股票,所以其利润为:--prices[0] dp[0][1] 第一天不持有股票,所以其利润为:0

4.遍历顺序:既然是前面的推出后面的,所以遍历顺序自然是从前往后

5.打印dp数组:通过打印dp数组检查最后结果的正确性

代码实现

class Solution {
    public int maxProfit(int[] prices) {
        //排除特殊情况
        if(prices.length==1){
            return 0;
        }
        //创建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];
    }
}

二.买卖股票的最佳时机II(可以买无数次)

题目描述(链接:力扣)

解题思路

我们仍然使用动规五部曲进行分析:

1.数组下标和其代表的含义:每一天同时也会有两种状态:持有和不持有股票,因此我们使用dp[n][2]作为dp数组,n代表哪一天,2代表两种不同的状态,dp[][]代表当天某种状态下的最大利润。

2.递推公式:dp[n][0]代表当天持有股票,则有以下两种可能性:①延续前面的有股票的状态②前一天没有股票,但是今天新买了股票,我们仍然取两者的最大值:dp[n][0]=max(dp[n-1][0],dp[n-1][1]-prices[i]),dp[n][1]代表当天不持有股票,同样有两种可能性:前一天持有但是今天卖掉了,或者延续前一天没有股票的状态,状态方程为:dp[n][1]=max(dp[n-1][1],dp[n-1][1]+prices[i])

3.初始化:第一天持有股票:dp[0][0]=-prices[0] 第一天不持有股票: dp[0][1]=0

4.遍历顺序:与买卖股票最佳时机1一致,从前往后遍历

5.打印:通过打印数组,确认结果的正确性

代码实现

class Solution {
    public int maxProfit(int[] prices) {
        //创建dp数组
        int[][]dp=new int[prices.length][2];
        //初始化
        dp[0][0]=-prices[0];
        //进行遍历
        for(int i=1;i<prices.length;++i){
            dp[i][0]=Math.max(dp[i-1][1]-prices[i],dp[i-1][0]);
            dp[i][1]=Math.max(dp[i-1][0]+prices[i],dp[i-1][1]);
        }
        return dp[prices.length-1][1];
    }
}

三.买卖股票的最佳时机III(只能买两次)

题目描述(链接:力扣)

解题思路

与前两个买卖股票II应用场景有所不同,这个题目对买卖股票添加了限制,限制买卖股票的次数(只允许买卖两次), 看似与买卖股票一类似(买卖股票1只允许买卖一次),但是其场景却变化了很多:买卖股票I与II的区别只是在递推公式中的买入股票时的利润(I是dp[n][0]=max(dp[n-1][0],-prices[i]]),II是:dp[n][0]=max(dp[n-1][0],dp[n-1][1]-prices[i])),但是一旦限制股票只能买卖两次,其场景就复杂起来了,这时需要重新定义1.数组下标及其含义dp数组下标及其本身的含义:dp[n][0]:第n天没有进行任何操作:dp[n][1]:第一次持有股票 dp[n][2]:第一次不持有股票 dp[n][3]:第二次持有股票dp[n][4] 第二次不持有股票。

2.递推公式:每一种状态下的递推公式也有所不同,我们一一进行解释:dp[n][1]=max(dp[n-1][1],dp[n-1][0]-prices[i]) (前一天第一次持有股票的状态或者前一天没有购买股票,今天刚购买),dp[n][2]=max(dp[n-1][2],dp[n-1][1]+prices[i])(延续前一天第一次不持有的状态或者前一天持有,今天抛售)dp[n][3]=max(dp[n-1][3],dp[n-1][2]-prices[i])(延续前一天的第二次持有或者前一天没有持有,今天刚购买)dp[n][4]=max(dp[n-1][4],dp[n-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) {
        //定义数组
        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];
    }
}

四.买卖股票的最佳时机IV(只能买k次)

题目描述(链接:力扣)

解题思路

解题思路与买卖股票III类似了:买卖股票III加上什么也不做的状态是定义了5种状态(未操作、第一次买入、第一次卖出、第二次买、第二次卖出),所以买卖股票IV相对于III而言,只有一点需要变化,那就是每一天的状态增加了:所以我们采用循环的方式,如下:其中k代表最多允许买卖的次数

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]);
            }

我们直接给出完整的代码(与买卖股票III类似)

class Solution {
    public int maxProfit(int k, int[] prices) {
        //创建数组
        int[][]dp=new int[prices.length][2*k+1];
        //初始化
        for(int i=0;i<2*k;i+=2){
            dp[0][i+1]=-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];
    }
}

五.买卖股票的最佳时机V(含冷冻期)

题目描述(链接:力扣)

解题思路

与前面题目描述有所不同的是,这次的股票问题存在一个冷冻期,也正是这个冷冻期的存在,我们dp数组对应的下标及dp数组的含义都要发生一定的变化:我们之前只是将每一天的状态分为持有股票和不持有股票两种(不持有股票包含了今天刚卖掉股票和延续之前卖掉股票的状态乳癌),但是一旦引入冷冻期这个概念,我们就必须将卖出股票的状态进行细分:1.今天刚卖出股票2.处于冷冻期内(昨天卖出股票)3.之前卖出股票且不处于冷冻期内,所以总共的状态为4种,在此基础上加上持有股票的状态,使用动规五部曲对此进行分析:

①dp数组下标含义及其数组含义dp[i][0]:代表当前持有股票 dp[i][1]:代表当前保持卖出股票(最晚是前天卖出股票);dp[i][2]代表今天刚卖出股票;dp[i][3]代表当前正处于冷冻期内

②递推公式:dp[i][0]=max(d[i-1][0]max(dp[i-1][3]-prices[i],dp[i-1][1]-prices[i]));(今天手中有股票,可能是延续前一天手中有股票的状态和昨天处于冷冻期内,今天买股票,或者昨天保持卖出股票的状态,今天买入股票) ;dp[i][1]=max(dp[i-1][3],dp[i-1][1])(保持前面的卖出股票状态或者昨天处于冷冻期,今天进入保持卖出股票) dp[i][2]=d[i][0]+prices[i](今天刚卖出股票,意味着前一天手里一定有股票,所以dp[i][2]只和前一天的dp[i-1][0]有关);dp[i][3]=dp[i-1][2](今天处于冷冻期内意味着一定是昨天刚卖出股票,所以只由昨天的dp[i-1][2]决定)

③初始化:dp[0][0]=-prices[0](今天刚买股票) dp[0][1]=0(在第0天存在这个状态实际上是不合法的,我们可以将其理解为今天手中没有股票,所以初始化为0) dp[0][2]=0(今天买了股票之后直接买卖出了,所以利润为0) dp[0][3]=0(今天处于冷冻期的状态也是不合法的,我们将其初始化为0)

④遍历顺序:依然是从前面推出后面,所以遍历顺序也是从前往后

⑤ 打印数组:通过打印dp数组判断合法性

代码实现

class Solution {
    public int maxProfit(int[] prices) {
        //创建数组
        int[][]dp=new int[prices.length][4];
        //进行初始化
        dp[0][0]=-prices[0];//0代表持有股票
        dp[0][1]=0;//1代表保持卖出股票
        dp[0][2]=0;//2表示卖出股票
        dp[0][3]=0;//3表示在冷冻期内
        //进行遍历循环
        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][1]-prices[i]));
         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[prices.length-1][1],Math.max(dp[prices.length-1][2],dp[prices.length-1][3]));
    }
}

六.买卖股票的最佳时机VI(含手续费)

题目描述(链接:力扣)

解题思路

本题与买卖股票II基本一致,唯一的区别在于我们在卖出股票的时候需要加上手续费,所以体现在代码上就是递推公式发生了变化:

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

其他与买卖股票II完全一致:我们直接给出代码. 

代码实现

class Solution {
    public int maxProfit(int[] prices, int fee) {
        //定义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][1]-prices[i],dp[i-1][0]);
            dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]+prices[i]-fee);
        }
        return dp[prices.length-1][1];
    }
}

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

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

相关文章

中科院发布多模态 ChatGPT,图片、语言、视频都可以 Chat ?中文多模态大模型力作

作者 | 小戏、ZenMoore 在 GPT-4 的发布报道上&#xff0c;GPT-4 的多模态能力让人印象深刻&#xff0c;它可以理解图片内容给出图片描述&#xff0c;甚至能在图片内容的基础上理解其中的隐喻或推断下一时刻的发展。无疑&#xff0c;面向所谓的 AGI&#xff08;通用人工智能&am…

数据结构初阶(1)(一些学习数据结构所需掌握的先导知识:包装类、装箱与拆箱、泛型、List简介)

包装类 基本数据类型和包装类是Java中处理数据的两种不同方式。 基本数据类型&#xff08;Primitive Types&#xff09;&#xff1a; Java的基本数据类型是直接存储数据的原始类型&#xff0c;包括以下8种类型&#xff1a; byte&#xff1a;1字节&#xff0c;用于表示整数 …

IEEE编写LaTeX时在作者后添加ORCID标志及链接(简单方案,一行代码)

IEEE的一些论文&#xff0c;如Trans系列惯例是要在作者后添加ORCID标志及链接&#xff0c;但是其How to里面没有相关latex代码案例。 1. 可以用但复杂的方案 CSDN中不少博主也给出了挺漂亮但是比较复杂的方案&#xff0c;如这个的一大串&#xff1a; \documentclass[letters…

Linux文本之awk编译器

一、awk介绍 1&#xff09;awk概述 AWK 是一种用于处理文本的编程语言工具。AWK 在很多方面类似于 shell 编程语言&#xff0c;尽管 AWK 具有完全属于其本身的语法。它的设计思想来源于 SNOBOL4 、sed 、Marc Rochkind设计的有效性语言、语言工具 yacc 和 lex &#xff0c;当…

尚硅谷大数据技术NiFi教程-笔记02【NiFi(使用案例,同步文件、离线同步mysql数据到hdfs、实时监控kafka数据到hdfs)】

尚硅谷大数据技术-教程学习路线-笔记汇总表【课程资料下载】 视频地址&#xff1a;尚硅谷大数据NiFi教程&#xff08;从部署到开发&#xff09;_哔哩哔哩_bilibili 尚硅谷大数据技术NiFi教程-笔记01【NiFi&#xff08;基本概念、安装、使用&#xff09;】尚硅谷大数据技术NiFi教…

探索古文明,玛雅文明衰落的原因

说起玛雅文明&#xff0c;大家在各种小说或者电影中或多或少的都有听说过&#xff0c;那么这个文明到底是怎么一回事呢&#xff1f;今天老铁就带大家好好的了解下。 玛雅文明存在的时间大致是在公元前2000年至公元1500年之间&#xff0c;这个文明见证了中美洲地区的一段辉煌的…

Cefsharp109.1.110(winfrom)最新支持H264-MP3-MP4功能体验,导出pdf和下载方法有变调整

最新版的支持H264版本(109.1.11,109.1.18)5154分支,也是win7/8/8.1最后一个支持版本 此贴仅分项版本变化和注意事项,本篇文章不提供dll编译文件,有需要单独联系,仅供学习参考 109版本体验测试(音频和视频功能),版本较100.0.230变化提醒及注意变更的内容。 上视频支…

C++每日一练:难题-大数加法

文章目录 前言一、题目二、代码及思路总结 前言 这题好像是指定了C&#xff0c;那就用C来做嘛&#xff0c;确实在C/C中一不小心就超出范围了&#xff0c;说实在的&#xff0c;C这个语言有时候真的很让人无语。很显然这是要用字符串来计算了。这题坑比较多&#xff0c;笔者这也…

数据库使用自增ID好还是UUID好?为什么?

数据库使用自增ID好还是UUID好&#xff1f;为什么&#xff1f; 答&#xff1a; 自增ID 优点&#xff1a; 数字类型&#xff0c;占用空间小数据库自动增量排序&#xff0c;对检索有利&#xff0c;读写速度快&#xff08;聚簇索引的优势&#xff09;系统编码过程中&#xff0…

知识推理学习笔记

OWL本体语言 基于RDF语法&#xff0c;最规范&#xff0c;最严谨&#xff0c;表达能力最强 一 语法 三元组 二 逻辑基础 描述逻辑&#xff1a;基于对象的知识表示的形式化&#xff0c;是一阶谓词逻辑的一个可判定子集 三 描述逻辑系统 一个描述逻辑包含4个基本组成部分 …

【Python数据存储】零基础也能轻松掌握的学习路线与参考资料

Python是一种高级编程语言&#xff0c;被广泛应用于数据科学中。数据存储是数据科学中至关重要的一环&#xff0c;因为人们需要将收集到的数据保存在一些地方。Python中的数据存储有很多种&#xff0c;因此在学习过程中需要明确自己的需求&#xff0c;掌握不同数据存储方式的优…

深入理解 python 虚拟机:多继承与 mro

深入理解 python 虚拟机&#xff1a;多继承与 mro 在本篇文章当中将主要给大家介绍 python 当中的多继承和 mro&#xff0c;通过介绍在多继承当中存在的问题就能够理解在 cpython 当中引入 c3 算法的原因了&#xff0c;从而能够帮助大家更好的了理解 mro 。 python 继承的问题…

【Linux】shell编程之—函数

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、概述二、函数的查看和删除1.查看 declare2.删除 declare 三、函数的返回值1.return 返回值2.echo 返回值 四、函数的参数传入与变量范围五、函数的应用1.阶乘2.…

十五、Gateway网关

目录 Zuul网关和gateway网关的区别&#xff1a; Gateway路由配置 1、新建服务网关项目&#xff0c;并在项目pom文件中引入gateway网关依赖 2、在application.yml配置gateway 3、如果不用配置的方式配置gateway路由&#xff0c;还可以通过代码的形式配置 4、启动网关服务&…

达梦:创建用户并授予用户只读权限

需求描述&#xff1a; 1.想创建一个用户&#xff0c;这用户能访问其他3个用户的资源&#xff0c;权限是只读&#xff0c;这种创用户的sql怎么写&#xff1f; 2.怎么修改用户密码呢&#xff1f; 环境&#xff1a; 通用机 一、创建用户并授权 1.创建业务用户步骤 step1:创建用户使…

短期光伏发电量短期预测(Python代码,基于LSTM模型)

一.代码流程&#xff08;运行视频&#xff1a;短期光伏发电量短期预测&#xff08;Python代码&#xff0c;基于LSTM模型&#xff09;_哔哩哔哩_bilibili&#xff09; 数据预处理&#xff1a; 读取CSV文件&#xff0c;并使用Pandas库将数据加载到DataFrame中。将时间列转换为日期…

PoseiSwap缘何成DEX赛道新宠?POSE价值分析

区块链技术以去中心化、伪匿名以及公开透明作为主要特点&#xff0c;虽然这种意识形态是具备先进性的&#xff0c;但以此为基础所带来的加密原生特性&#xff0c;也正在阻碍着链上世界的发展。作为一种透明、非许可的分布式网络&#xff0c;隐私与合规始终是现阶段&#xff0c;…

【AWK命令】

目录 一、awk的工作原理&#xff1a;二、命令演示1、按行输出文本2、按字段输出文本1、使用三元运算符 三、通过管道&#xff0c;双引号调用shell命令1、查看时间的命令2、getline 获取内容3、OFS输出分割符4、awk来排序数组 一、awk的工作原理&#xff1a; 1、逐行读取文本&a…

华为OD机试真题 Java 实现【日志采集系统】【2023Q1 100分】

一、题目描述 日志采集是运维系统的的核心组件。日志是按行生成&#xff0c;每行记做一条&#xff0c;由采集系统分批上报。 如果上报太频繁&#xff0c;会对服务端造成压力&#xff1b;如果上报太晚&#xff0c;会降低用户的体验&#xff1b;如果一次上报的条数太多&#xf…

MSQL系列(一) Mysql实战-索引结构 二叉树/平衡二叉树/红黑树/BTree/B+Tree

Mysql实战-索引结构 二叉树/平衡二叉树/红黑树/BTree/BTree 我们在项目中都会使用索引&#xff0c;所以我们要了解索引的存储结构&#xff0c;今天我们就着重讲解下Mysql的索引结构存储模型&#xff0c;并且看下 二叉树&#xff0c;平衡二叉树&#xff0c;红黑树&#xff0c;B…