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

这篇具有很好参考价值的文章主要介绍了动态规划:万变不离其宗,带你吃透股票系列问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

前言:

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

一.买卖股票的最佳时机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完全一致:我们直接给出代码. 文章来源地址https://www.toymoban.com/news/detail-447526.html

代码实现

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

到了这里,关于动态规划:万变不离其宗,带你吃透股票系列问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【动态规划专栏】--简单-- 动态规划经典题型

    目录 动态规划 动态规划思维(基础) 状态表示(最重要) 状态转移方程(最难) 初始化(细节) 填表顺序(细节) 返回值(结果) 解码方法⭐⭐ 【题目解析】   【算法原理】 C++ 算法代码 复杂度分析 【空间优化 - 滚动数组】 C++ 算法代码 复杂度分析 【DP边界、初始化

    2024年02月08日
    浏览(104)
  • 【动态规划】从入门到实践---动态规划详解

    目录 1.动态规划概念 一.定义数组元素的含义 二.找到数组元素之间的关系表达式 三.找到初始值 2.案例详解 一:爬楼梯 1.定义数组元素的含义 2.找到数组元素之间的关系表达式 3.找到初始值 案例二:最短路径 题目: 做题步骤: 1.定义数组的含义 2.找到数组元素之间的关系表

    2024年02月02日
    浏览(32)
  • 【算法 - 动态规划】原来写出动态规划如此简单!

    从本篇开始,我们就正式开始进入 动态规划 系列文章的学习。 本文先来练习两道通过 建立缓存表 优化解题过程的题目,对如何将 递归函数 修改成 动态规划 的流程有个基本的熟悉。 用最简单的想法完成题目要求的 递归 函数; 定义明确 递归函数 的功能!!! 分析是否存

    2024年02月21日
    浏览(48)
  • LeetCode练习七:动态规划上:线性动态规划

    参考《OI Wiki动态规划》、《算法通关手册》动态规划篇 1.1 动态规划简介    动态规划(Dynamic Programming) :简称 DP ,是一种通过把原问题分解为相对简单的子问题的方式而求解复杂问题的方法。 动态规划方法与分治算法类似,却又不同于分治算法。 「动态规划的核心思想

    2024年02月12日
    浏览(70)
  • 【动态规划专栏】-- 01 背包问题 -- 动态规划经典题型

    目录 背包问题概述 01 背包问题 01背包⭐⭐  【算法原理】 第一问 第二问 C++ 算法代码 复杂度分析 【空间优化 - 滚动数组】 C++ 算法代码 复杂度分析 分割等和子集⭐⭐ 【算法原理】  对于类01背包问题 C++ 算法代码  【空间优化 - 滚动数组】  C++ 算法代码 目标和⭐⭐ 【算

    2024年02月05日
    浏览(57)
  • 动态规划二:二维动态规划(18308+11077+19187+17089)

            线性动态规划只需考虑一个变量,而二维动规需要两个变量来实现动态规划方程。定义两个变量需要根据具体问题具体分析。常见的二维动规问题类型个人划分为以下几种类型:(1)字符串DP(2)N区间M问题(3)矩阵类DP问题(4) 区间动态规划(单独介绍) (5)

    2024年02月16日
    浏览(30)
  • 【动态规划专栏】-- 回文串问题 -- 动态规划经典题型

    目录 动态规划 动态规划思维(基础) 状态表示(最重要) 状态转移方程(最难) 初始化(细节) 填表顺序(细节) 返回值(结果) 回文子串 ⭐⭐ 【题目解析】  【算法原理】 C++ 算法代码  最长回文子串 ⭐⭐  【题目解析】  【算法原理】 C++ 算法代码   回文串分割

    2024年02月08日
    浏览(46)
  • 60题学会动态规划系列:动态规划算法第三讲

    简单多状态问题 文章目录 一.按摩师 二.打家劫舍系列 三.删除并获得点数 四.粉刷房子 力扣链接:力扣 一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,

    2024年02月08日
    浏览(49)
  • 动态规划入门之二维数组的动态规划(过河卒)

    P1002 [NOIP2002 普及组] 过河卒 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 过河卒,首先科普一下象棋里面的马的跳跃一步的规则吧(这题真够坑人的,连个规则都不给出,害得我第一次交就全wa)。一张图解释   大家看所有标记为p的点的坐标就是马跳一步和原来的位置。解决

    2024年02月12日
    浏览(40)
  • 动态规划课堂5-----子序列问题(动态规划 + 哈希表)

    目录 引言: 例题1:最长递增子序列 例题2:最长定差子序列 例题3:最长的斐波那契子序列的长度 例题4:最长等差数列 例题5:等差数列划分II-子序列 结语: 要想解决子序列问题那么就要理解子序列和子数组的区别,二者的定义如下。 子序列: 是由数组派生而来的序列,

    2024年03月13日
    浏览(75)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包