【算法优选】 动态规划之简单多状态dp问题——贰

这篇具有很好参考价值的文章主要介绍了【算法优选】 动态规划之简单多状态dp问题——贰。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

🎋前言

动态规划相关题目都可以参考以下五个步骤进行解答:

  1. 状态表示

  2. 状态转移⽅程

  3. 初始化

  4. 填表顺序

  5. 返回值

后面题的解答思路也将按照这五个步骤进行讲解。

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

🚩题目描述

给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

  • 示例 1:
    输入: prices = [1,2,3,0,2]
    输出: 3
    解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
  • 示例 2:
    输入: prices = [1]
    输出: 0
class Solution {
    public int maxProfit(int[] prices) {

    }
}

🚩算法思路:

🎈状态表示:

对于线性dp ,我们可以⽤「经验+题⽬要求」来定义状态表示:

  • 以某个位置为结尾…;
  • 以某个位置为起点…。

这里我们选择比较常⽤的⽅式,以某个位置为结尾,结合题目要求,定义⼀个状态表⽰:

由于有「买⼊」「可交易」「冷冻期」三个状态,因此我们可以选择⽤三个数组,其中:

  • dp[i][0] 表⽰:第 i 天结束后,处于「买⼊」状态,此时的最⼤利润;
  • dp[i][1]表⽰:第 i 天结束后,处于「可交易」状态,此时的最⼤利润;
  • dp[i][2] 表⽰:第 i 天结束后,处于「冷冻期」状态,此时的最⼤利润。

🎈状态转移方程:

我们要谨记规则:

  1. 处于「买⼊」状态的时候,我们现在有股票,此时不能买股票,只能继续持有股票,或者卖出股票;
  2. 处于「卖出」状态的时候:
    • 如果「在冷冻期」,不能买⼊;
    • 如果「不在冷冻期」,才能买⼊。

对于 dp[i][0] ,我们有「两种情况」能到达这个状态:

  1. 在 i - 1 天持有股票,此时最⼤收益应该和 i - 1 天的保持⼀致: dp[i - 1][0] ;
  2. 在 i 天买⼊股票,那我们应该选择 i - 1 天不在冷冻期的时候买⼊,由于买⼊需要花钱,所以此时最⼤收益为: dp[i - 1][1] - prices[i]

两种情况应取最⼤值,因此: dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]) 。

对于 dp[i][1] ,我们有「两种情况」能到达这个状态:

  1. 在 i - 1 天的时候,已经处于冷冻期,然后啥也不⼲到第 i 天,此时对应的状态为:dp[i - 1][2] ;
  2. 在 i - 1 天的时候,⼿上没有股票,也不在冷冻期,但是依旧啥也不干到第 i 天,此时对应的状态为 dp[i - 1][1] ;

两种情况应取最⼤值,因此: dp[i][1] = max(dp[i - 1][1], dp[i - 1][2]) 。

对于 dp[1][i] ,我们只有「⼀种情况」能到达这个状态:

  1. 在 i - 1 天的时候,卖出股票。
  2. 因此对应的状态转移为: dp[i][2] = dp[i - 1][0] + prices[i]

🎈初始化:

三种状态都会用到前⼀个位置的值,因此需要初始化每⼀行的第⼀个位置:

  • dp[0][0] :此时要想处于「买⼊」状态,必须把第⼀天的股票买了,因此 dp[0][0] = -prices[0] ;
  • dp[0][1] :啥也不用干即可,因此 dp[0][1] = 0 ;
  • dp[0][2] :⼿上没有股票,买⼀下卖⼀下就处于冷冻期,此时收益为 0 ,因此 dp[0][2]= 0 。

🎈填表顺序:

根据「状态表示」,我们要三个表⼀起填,每⼀个表「从左往右」。

🎈返回值:

应该返回「卖出状态」下的最⼤值,因此应该返回 max(dp[n - 1][1], dp[n - 1][2])

🚩代码实现

class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        int[][] dp = new int[n][3];
        dp[0][0] = - prices[0];
        for(int i = 1; i < n; 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][2]);
            dp[i][2] = dp[i - 1][0] + prices[i];
        }
        return Math.max(dp[n - 1][1], dp[n - 1][2]);
    }
}

🍃买卖股票的最佳时期含手续费(medium)

🚩题目描述

给定一个整数数组 prices,其中 prices[i]表示第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。

你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。

注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。

  • 示例 1:
    输入:prices = [1, 3, 2, 8, 4, 9], fee = 2
    输出:8
    解释:能够达到的最大利润:
    在此处买入 prices[0] = 1
    在此处卖出 prices[3] = 8
    在此处买入 prices[4] = 4
    在此处卖出 prices[5] = 9
    总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8
  • 示例 2:
    输入:prices = [1,3,7,5,10,3], fee = 3
    输出:6
class Solution {
    public int maxProfit(int[] prices, int fee) {

    }
}

🚩算法思路

🎈状态表示:

对于线性 dp ,我们可以⽤「经验 + 题⽬要求」来定义状态表⽰:

  1. 以某个位置为结尾,进行操作;
  2. 以某个位置为起点,进行操作。

这里我们选择比较常用的方式,以某个位置为结尾,结合题目要求,定义⼀个状态表示:

由于有「买⼊」「可交易」两个状态,因此我们可以选择用两个数组,其中:

  • f[i] 表示:第 i 天结束后,处于「买⼊」状态,此时的最大利润;
  • g[i] 表示:第 i 天结束后,处于「卖出」状态,此时的最大利润。

🎈状态转移方程:

我们选择在「卖出」的时候,支付这个手续费,那么在「买入」的时候,就不⽤再考虑⼿续费的问题。

对于 f[i] ,我们有两种情况能到达这个状态:

  1. 在 i - 1 天「持有」股票,第 i 天啥也不干。此时最⼤收益为 f[i - 1] ;
  2. 在 i - 1 天的时候「没有」股票,在第 i 天买⼊股票。此时最⼤收益为 g[i - 1] - prices[i])

两种情况下应该取最大值,因此 f[i] = max(f[i - 1], g[i - 1] -prices[i]) 。

对于 g[i] ,我们也有两种情况能够到达这个状态:

  1. 在 i - 1 天「持有」股票,但是在第 i 天将股票卖出。此时最大收益为: f[i - 1]+ prices[i] - fee) ,记得手续费;
  2. 在 i - 1 天「没有」股票,然后第 i 天啥也不干。此时最大收益为: g[i - 1] ;

两种情况下应该取最大值,因此 g[i] = max(g[i - 1], f[i - 1] + prices[i] - free) 。

class Solution {
    public int maxProfit(int[] prices, int fee) {
        int[] f = new int[prices.length];
        int[] g = new int[prices.length];
        f[0] = -prices[0];
        for (int i = 1; i < prices.length; i++) {
            f[i] = Math.max(f[i - 1],g[i - 1] - prices[i]);
            g[i] = Math.max(g[i - 1], f[i - 1] + prices[i] - fee); 
        }
        return g[prices.length - 1];
    }
}

🎈初始化:

由于需要用到前面的状态,因此需要初始化第⼀个位置。

  • 对于 f[0] ,此时处于「买入」状态,因此 f[0] = -prices[0] ;
  • 对于 g[0] ,此时处于「没有股票」状态,啥也不干即可获得最大收益,因此 g[0] = 0

🎈填表顺序:

毫⽆疑问是「从左往右」,但是两个表需要⼀起填。

🎈返回值:

应该返回「卖出」状态下,最后⼀天的最大值收益: g[n - 1]

🚩代码实现

class Solution {
    public int maxProfit(int[] prices, int fee) {
        int[] f = new int[prices.length];
        int[] g = new int[prices.length];
        f[0] = -prices[0];
        for (int i = 1; i < prices.length; i++) {
            f[i] = Math.max(f[i - 1],g[i - 1] - prices[i]);
            g[i] = Math.max(g[i - 1], f[i - 1] + prices[i] - fee); 
        }
        return g[prices.length - 1];
    }
}

🌳买卖股票的最佳时机 III

🚩题目描述

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

  • 示例 1:
    输入:prices = [3,3,5,0,0,3,1,4]
    输出:6
    解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
    随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。
    -示例 2:
    输入:prices = [1,2,3,4,5]
    输出:4
    解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
    注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
    因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
  • 示例 3:
    输入:prices = [7,6,4,3,1]
    输出:0
    解释:在这个情况下, 没有交易完成, 所以最大利润为 0。
  • 示例 4:
    输入:prices = [1]
    输出:0
class Solution {
    public int maxProfit(int[] prices) {

    }
}

🚩算法思路

🎈状态表示:

对于线性 dp ,我们可以⽤「经验 + 题目要求」来定义状态表示:

  1. 以某个位置为结尾,进行一系列操作;
  2. 以某个位置为起点,进行一系列操作。

这里我们选择比较常⽤的⽅式,以某个位置为结尾,结合题目要求,定义⼀个状态表⽰:

由于有「买⼊」「可交易」两个状态,因此我们可以选择用两个数组。但是这道题⾥⾯还有交易次数的限制,因此我们还需要再加上⼀维,用来表示交易次数。其中:

  • f[i][j] 表⽰:第 i 天结束后,完成了 j 次交易,处于「买⼊」状态,此时的最⼤利润;

  • g[i][j] 表⽰:第 i 天结束后,完成了 j 次交易,处于「卖出」状态,此时的最⼤利润。

🎈状态转移方程:

对于 f[i][j] ,我们有两种情况到这个状态:

  1. 在 i - 1 天的时候,交易了 j 次,处于「买⼊」状态,第 i 天啥也不干即可。此时最大利润为: f[i - 1][j] ;
  2. 在 i - 1 天的时候,交易了 j 次,处于「卖出」状态,第 i 天的时候把股票买了。此时的最⼤利润为: g[i - 1][j] - prices[i] 。

综上,我们要的是「最⼤利润」,因此是两者的最⼤值: f[i][j] = max(f[i - 1][j],g[i - 1][j] - prices[i]) 。

对于 g[i][j] ,我们也有两种情况可以到达这个状态:

  1. 在 i - 1 天的时候,交易了 j 次,处于「卖出」状态,第 i 天啥也不⼲即可。此时的最大利润为: g[i - 1][j] ;
  2. 在 i - 1 天的时候,交易了 j - 1 次,处于「买⼊」状态,第 i 天把股票卖了,然后就完成了 j ⽐交易。此时的最⼤利润为: f[i - 1][j - 1] + prices[i] 。但是这个状态不⼀定存在,要先判断⼀下。

综上,我们要的是最⼤利润,因此状态转移方程为:

g[i][j] = g[i - 1][j];
if(j >= 1) {
	g[i][j] = max(g[i][j], f[i - 1][j - 1] + prices[i]);
}

🎈初始化:

由于需要用到 i = 0 时的状态,因此我们初始化第⼀行即可。

  • 当处于第 0 天的时候,只能处于「买⼊过⼀次」的状态,此时的收益为 -prices[0] ,因此 f[0][0] = - prices[0] 。
  • 为了取 max 的时候,⼀些不存在的状态「起不到干扰」的作用,我们统统将它们初始化为 - INF (用 INT_MIN 在计算过程中会有「溢出」的风险,这⾥ INF 折半取0x3f3f3f3f ,足够小即可)

🎈填表顺序:

从「上往下填」每⼀行,每⼀行「从左往右」,两个表「⼀起填」。

🎈返回值:

返回处于「卖出状态」的最⼤值,但是我们也「不知道是交易了⼏次」,因此返回 g 表最后⼀行的最大值。

🚩代码实现

class Solution {
    public int maxProfit(int[] prices) {
        // 1. 创建 dp 表
        // 2. 初始化
        // 3. 填表
        // 4. 返回值
        int INF = 0x3f3f3f3f;
        int n = prices.length;
        int[][] f = new int[n][3];
        int[][] g = new int[n][3];
        for(int j = 0; j < 3; j++) {
            f[0][j] = g[0][j] = -INF;
        }
        f[0][0] = -prices[0];
        g[0][0] = 0;
        for(int i = 1; i < n; i++) {
            for (int j = 0; j < 3; j++) {
                f[i][j] = Math.max(f[i - 1][j], g[i - 1][j] - prices[i]);
                g[i][j] = g[i - 1][j];
                // 判断状态是否存在
                if (j - 1 >= 0) {
                    g[i][j] = Math.max(g[i][j], f[i - 1][j - 1] + prices[i]);
                }
            }
        }
        //找出最后一行的最大值
        int ret = 0; 
        for(int j = 0; j < 3; j++) {
            ret = Math.max(ret, g[n - 1][j]);
        }
        return ret;
    }
}

⭕总结

关于《【算法优选】 动态规划之简单多状态dp问题——贰》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下!文章来源地址https://www.toymoban.com/news/detail-848844.html

到了这里,关于【算法优选】 动态规划之简单多状态dp问题——贰的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 【动态规划】简单多状态dp问题(1)打家劫舍问题

    打家劫舍问题 传送门:面试题 17.16. 按摩师 题目: 1.1 题目解析 越难的dp问题,看示例只能起到了解题目的效果,一般推不出啥普遍的规律,所以接下来就是我们的算法原理,通过动归的思想去理解,才会豁然开朗! 1.2 算法原理 1.2.1 状态表示 我们需要通过经验 + 题目要求去

    2024年02月12日
    浏览(40)
  • 【动态规划】12简单多状态dp问题_打家劫舍II_C++ (medium)

    题目链接:leetcode打家劫舍II 目录 题目解析: 算法原理 1.状态表示 2.状态转移方程 3.初始化 4.填表顺序 5.返回值 编写代码 题目让我们求 在不触动警报装置的情况下  ,能够偷窃到的最高金额。 由题可得: 第一个房屋和最后一个房屋是紧挨着的 如果两间相邻的房屋在同一晚

    2024年02月02日
    浏览(46)
  • 【算法优选】 动态规划之路径问题——贰

    动态规划相关题目都可以参考以下五个步骤进行解答: 状态表⽰ 状态转移⽅程 初始化 填表顺序 返回值 后面题的解答思路也将按照这五个步骤进行讲解。 给你一个 n x n 的 方形 整数数组 matrix ,请你找出并返回通过 matrix 的下降路径 的 最小和 。 下降路径 可以从第一行中的

    2024年02月05日
    浏览(48)
  • 【动态规划专栏】专题三:简单多状态dp--------3.删除并获得点数

    本专栏内容为:算法学习专栏,分为优选算法专栏,贪心算法专栏,动态规划专栏以及递归,搜索与回溯算法专栏四部分。 通过本专栏的深入学习,你可以了解并掌握算法。 💓博主csdn个人主页:小小unicorn ⏩专栏分类:动态规划专栏 🚚代码仓库:小小unicorn的代码仓库🚚

    2024年03月22日
    浏览(42)
  • acwing算法基础之动态规划--数位统计DP、状态压缩DP、树形DP和记忆化搜索

    暂无。。。 暂无。。。 题目1 :求a~b中数字0、数字1、…、数字9出现的次数。 思路:先计算1~a中每位数字出现的次数,然后计算1~b-1中每位数字出现的次数,两个相减即是最终答案。 那么,如何计算1~a中每位数字出现的次数呢? 首先,将a的每一位存入向量num中,例如a=123

    2024年02月04日
    浏览(49)
  • AcWing算法学习笔记:动态规划(背包 + 线性dp + 区间dp + 计数dp + 状态压缩dp + 树形dp + 记忆化搜索)

    算法 复杂度 时间复杂度0(nm) 空间复杂度0(nv) 代码 算法 通过滚动数组对01背包朴素版进行空间上的优化 f[i] 与 f[i - 1]轮流交替 若体积从小到大进行遍历,当更新f[i, j]时,f[i - 1, j - vi] 已经在更新f[i, j - vi]时被更新了 因此体积需要从大到小进行遍历,当更新f[i, j]时,f[i - 1,

    2024年02月21日
    浏览(42)
  • 【算法】动态规划(dp问题),持续更新

    介绍本篇之前,我想先用人话叙述一般解决动态规划问题的思路: 动态规划的问题,本身有许多产生结果的可能,需要在具体题目下得到满足某个条件的解。 如何得到呢? 我们就需要根据这个具体问题,建立一个状态表( dp 表 ),在这张 dp 表中的每一个位置的数据都有明

    2024年02月04日
    浏览(48)
  • 【算法学习】简单多状态-动态规划

            本篇博客记录动态规划中的简单多状态问题。         在之前的动态规划类型的题中,我们每次分析的都只是一种或者某一类的状态,定义的dp表也是围绕着一种状态来的。         现在可能对于一种状态,存在几种不同的子状态,在状态转移过程中相互影响。此时

    2024年01月18日
    浏览(39)
  • C++ DP算法,动态规划——背包问题(背包九讲)

    有N件物品和一个容量为 V V V 的背包。放入第i件物品耗费的空间是 C i C_i C i ​ ,得到的价值是 W i W_i W i ​ 。 求解将哪些物品装入背包可使价值总和最大。 这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。 用子问题定义状态:即 F [ i , v ] F[i, v] F

    2024年02月16日
    浏览(47)
  • 算法第十五期——动态规划(DP)之各种背包问题

    目录 0、背包问题分类 1、 0/1背包简化版 【代码】 2、0/ 1背包的方案数 【思路】

    2023年04月09日
    浏览(37)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包