【动态规划】简单多状态dp问题

一、经验总结

在分析dp问题的状态表示时,发现当前阶段的状态可以继续细分为多个状态,且多个状态之间可以通过某种方式进行转换,这就是动态规划的多状态问题。

多状态问题的关键有以下几点:

  1. 找出dp问题的多个状态表示:经验+题目要求,在之前经验的基础上(以i位置为起点或终点)继续对当前阶段的状态进行细分。为每种状态各自创建出一个dp表。每种状态可能还有若干子状态,此时应该为每种状态的dp表增加维度,以表示出所有的子状态。

  2. 确定多个状态间的相互转换关系:对于简单题目可以直接得到转换关系(前4题);但是如果每个阶段的状态数量较多,状态间的相互转换较为复杂,可以借助状态机模型分析各状态间的转换关系(后4题),其中:

    1. 各顶点表示的是每个阶段的各种状态(在第一步确定状态表示时得出)
    2. 箭头的起点是前一阶段的状态
    3. 箭头的终点是当前阶段的状态
    4. 箭头上的动作是状态转换时需要进行的活动(当前阶段)

    如何绘制状态机?

    1. 先画出每个阶段的各种状态
    2. 对于每种状态,先考虑自己能不能转换为自己(状态不改变)
    3. 再考虑其他状态能不能转换为该状态
    4. 标出状态转换时需要进行的活动(也可以啥也不干)
  3. 之后就可以依据转换关系写出各种状态的状态转移方程了。最好依据绘制状态机的顺序写方程,做到不重不漏的计算出所有状态的结果。

  4. 填表顺序:要按照顺序将创建的多个dp表一起填

  5. 返回值:要考虑到所有的状态


二、相关编程题

2.1 按摩师

题目链接

面试题 17.16. 按摩师 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

编写代码

class Solution {
public:
    int massage(vector<int>& nums) {
        //1.创建dp表
        //2.初始化
        //3.填表
        //4.返回值
        int n = nums.size();
        if(n == 0) return 0;
        vector<int> dp1(n), dp2(n);
        dp1[0] = nums[0];
        for(int i = 1; i < n; ++i)
        {
            dp1[i] = dp2[i-1] + nums[i];
            dp2[i] = max(dp1[i-1], dp2[i-1]);
        }
        return max(dp1[n-1], dp2[n-1]);
    }
};

2.2 打家劫舍Ⅱ

题目链接

LCR 090. 打家劫舍 II - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

编写代码

class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        return max(rob1(nums, 2, n-1) + nums[0], rob1(nums, 1, n));
    }

    int rob1(vector<int>& nums, int begin, int end) {
        if(begin >= end) return 0;
        int n = end-begin; //左闭右开
        vector<int> dp1(n), dp2(n); //dp1偷/dp2不偷
        dp1[0] = nums[begin];
        for(int i = 1; i < n; ++i)
        {
            dp1[i] = dp2[i-1] + nums[begin+i]; //注意下标映射关系
            dp2[i] = max(dp1[i-1], dp2[i-1]);
        }
        return max(dp1[n-1], dp2[n-1]);
    }
};

2.3 删除并获得点数

题目链接

740. 删除并获得点数 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

编写代码

class Solution {
public:
    int deleteAndEarn(vector<int>& nums) {
        const int N = 10001;
        int arr[N] = {0}; //该数组用于统计每个数字出现的总和
        for(auto e : nums)
            arr[e]+=e;
        vector<int> dp1(N), dp2(N); //dp1获取,dp2不获取
        dp1[1] = arr[1];
        for(int i = 2; i < N; ++i)
        {
            dp1[i] = dp2[i-1]+arr[i];
            dp2[i] = max(dp1[i-1], dp2[i-1]);
        }
        return max(dp1[N-1], dp2[N-1]);
    }
};

2.4 粉刷房子

题目链接

LCR 091. 粉刷房子 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

编写代码

class Solution {
public:
    int minCost(vector<vector<int>>& costs) {
        int n = costs.size();
        vector<vector<int>> dp(n+1, vector<int>(3));
        for(int i = 1; i <= n; ++i)
        {
            dp[i][0] = min(dp[i-1][1], dp[i-1][2])+costs[i-1][0];
            dp[i][1] = min(dp[i-1][0], dp[i-1][2])+costs[i-1][1];
            dp[i][2] = min(dp[i-1][1], dp[i-1][0])+costs[i-1][2];
        }
        return min(dp[n][0], min(dp[n][1], dp[n][2]));
    }
};

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

题目链接

309. 买卖股票的最佳时机含冷冻期 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

如果每个阶段的状态数量较多,状态间的相互转换较为复杂,可以借助状态机模型分析各状态间的转换关系,其中:

  1. 各顶点表示的是每个阶段的各种状态(在第一步确定状态表示时得出)
  2. 箭头的起点是前一阶段的状态
  3. 箭头的终点是当前阶段的状态
  4. 箭头上的动作是状态转换时需要进行的活动(当前阶段)

如何绘制状态机?

  1. 先画出每个阶段的各种状态
  2. 对于每种状态,先考虑自己能不能转换为自己(状态不改变)
  3. 再考虑其他状态能不能转换为该状态
  4. 标出状态转换时需要进行的活动

编写代码

class Solution {
public:
    int maxProfit(vector<int>& p) {
        int n = p.size();
        vector<vector<int>> dp(n, vector<int>(3));
        dp[0][0] = -p[0];
        for(int i = 1; i < n; ++i)
        {
            dp[i][0] = max(dp[i-1][0], dp[i-1][1]-p[i]);
            dp[i][1] = max(dp[i-1][1], dp[i-1][2]);
            dp[i][2] = dp[i-1][0]+p[i];
        }
        return max(dp[n-1][1], dp[n-1][2]);
    }
};

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

题目链接

714. 买卖股票的最佳时机含手续费 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

编写代码

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int n = prices.size();
        vector<int> f(n), g(n); //f是买入状态,g是可交易状态
        f[0] = -prices[0];
        for(int i = 1; i < n; ++i)
        {
            f[i] = max(f[i-1], g[i-1]-prices[i]);
            g[i] = max(g[i-1], f[i-1]+prices[i]-fee);
        }
        return g[n-1]; //f[n-1]最后一天还有股票没有卖出,一定不是最大利润,不考虑
    }
};

2.7 买卖股票的最佳时机III

题目链接

123. 买卖股票的最佳时机 III - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

为什么INF(无穷大)用0x3f3f3f3f表示?请阅读文章:0x3f3f3f3f是什么意思-CSDN博客

用0x3f3f3f3f表示32-bit int最大值的好处:

  1. 足够大:0x3f3f3f3f是10^9级别的,比一般场合下的数据都要大,所以它可以作为无穷大使用,而不至于出现数据大于无穷大的情况。
  2. 加数据不会溢出:0x3f3f3f3f加上一个数据时并不会溢出,也就是说“无穷大加上一个有穷的数依然是无穷大”。甚至0x3f3f3f3f + 0x3f3f3f3f依然没有超过32-bit int的表示范围,也就满足了我们“无穷大加无穷大还是无穷大的需求”。
  3. 可以使用memset设置内存:之前如果我们要将某个int数组全部赋值为INT_MAX,需要自己写循环,不能使用memset,因为memset是以字节为单位进行赋值的。现在好了,如果我们将无穷大设为0x3f3f3f3f,由于它的每个字节都是0x3f,我们可以通过memset(arr, 0x3f, sizeof(arr));将数组全部置为无穷大。

编写代码

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        int INF = 0x3f3f3f3f; //无穷大
        vector<vector<int>> f(n, vector<int>(3, -INF)), g(n, vector<int>(3, -INF));
        //边界1:第0天第0次交易,其余交易次数的收益初始化为-∞
        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] = max(f[i-1][j], g[i-1][j]-prices[i]);
                //边界2:第0次交易时卖出状态
                if(j == 0) 
                    g[i][j] = 0;
                else
                    g[i][j] = max(g[i-1][j], f[i-1][j-1]+prices[i]);
            }
        }   
        return max(g[n-1][0], max(g[n-1][1], g[n-1][2]));
    }
};

2.8 买卖股票的最佳时机Ⅳ

题目链接

188. 买卖股票的最佳时机 IV - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

编写代码

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        int n = prices.size();
        k = min(k, n/2); //n天最多进行n/2次有效交易
        const int INF = 0x3f3f3f3f;
        vector<vector<int>> f(n, vector<int>(k+1, -INF));
        auto g = f;
        f[0][0] = -prices[0];
        g[0][0] = 0;
        for(int i = 1; i < n; ++i)
        {
            for(int j = 0; j <= k; ++j)
            {
                f[i][j] = max(f[i-1][j], g[i-1][j]-prices[i]);
                if(j == 0)
                    g[i][j] = 0;
                else
                    g[i][j] = max(g[i-1][j], f[i-1][j-1]+prices[i]);
            }
        }
        int ret = -INF;
        for(int j = 0; j <= k; ++j)
        {
            ret = max(ret, g[n-1][j]);
        }
        return ret;
    }
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MATLAB-SSA-CNN-SVM,基于SSA麻雀优化算法优化卷积神经网络CNN结合支持向量机SVM数据分类(多特征输入多分类)

MATLAB-SSA-CNN-SVM,基于SSA麻雀优化算法优化卷积神经网络CNN结合支持向量机SVM数据分类(多特征输入多分类) 1.数据均为Excel数据&#xff0c;直接替换数据就可以运行程序。 2.所有程序都经过验证&#xff0c;保证程序可以运行。 3.具有良好的编程习惯&#xff0c;程序均包含…

Vue.JS中如何监听生命周期事件?

目录 一、Vue.JS框架介绍二、Vue.JS的监听事件三、Vue.JS的生命周期事件四、Vue.JS中如何监听生命周期事件 一、Vue.JS框架介绍 Vue.js是一个用于构建用户界面的渐进式JavaScript框架。它设计得非常灵活&#xff0c;可以轻松地被集成到现有的项目中&#xff0c;也可以作为一个…

52、U-boot2023的移植教程

uboot&#xff1a;https://ftp.denx.de/pub/u-boot/ nxp-uboot&#xff1a;https://github.com/nxp-imx/uboot-imx 1、顶层Makefile 文件加入编译的两种方式&#xff1a;以xxx/xxx.c文件为例 1、使用menuconfig: 先编辑.c所在目录下的Kconfig&#xff0…

CCS提示No XDCtools,equivalent...怎么办

摘要&#xff1a;本文介绍CCS( Version: 12.7.0.00007 )编译TI毫米波雷达遇到的No XDCtools&#xff0c;equivalent to the specified version 3.50.8.24_core,are available - defaulting to 3.62.1.16_core.问题的解决方法。 解决这个问题的方法是下载所需要的版本。上图所示…

38 - 换座位(高频 SQL 50 题基础版)

38 - 换座位 -- 方法一 select(casewhen id%21 and id(select max(id) from seat) then idwhen id%20 then id-1else id1end) as id, student fromseat order byid;-- 方法二selectif(id%20,id-1,if(id(select max(id) from Seat),id,id1)) as id,student fromSeat order by id…

1996年-2023年 全国298个地级市-外商直接投资FDI(数据收集)

外商直接投资&#xff08;FDI&#xff09;是一种跨国界的经济活动&#xff0c;它涉及外国投资者在中国境内进行的直接投资行为。这种投资行为不仅包括以货币、实物、技术等形式的资本投入&#xff0c;还可能包括开办独资企业、合资企业、合作企业&#xff0c;以及参与资源开发等…

【网络安全常用术语解读 :什么是0day、1day、nday漏洞】

脆弱性攻击的时间窗被称作脆弱性窗口。通常情况下&#xff0c;一个安全漏洞的时间越久&#xff0c;攻击者就会有更多的机会去攻击它。 2. 0day 漏洞 0天漏洞&#xff0c;也被称作"零日漏洞"&#xff0c;是指尚未由供应商公布的缺陷&#xff0c;表示攻击者已知晓该缺…

CentOS 7、Debian、Ubuntu,这些是什么意思

CentOS 7、Debian、Ubuntu 都是基于 Linux 内核的操作系统&#xff0c;它们各自有不同的特性和用途。以下是对它们的详细解释&#xff1a; CentOS 7 CentOS&#xff08;Community ENTerprise Operating System&#xff09; 是一个基于开源的 Linux 发行版。CentOS 7 是 CentOS …

摄像头画面显示于unity场景

&#x1f43e; 个人主页 &#x1f43e; &#x1faa7;阿松爱睡觉&#xff0c;横竖醒不来 &#x1f3c5;你可以不屠龙&#xff0c;但不能不磨剑&#x1f5e1; 目录 一、前言二、UI画面三、显示于场景四、结语 一、前言 由于标题限制&#xff0c;这篇文章主要是讲在unity中调用摄…

基于JSP的教学质量评价系统

开头语&#xff1a; 你好&#xff0c;我是计算机学长猫哥。如果您对教学质量评价系统感兴趣或有相关需求&#xff0c;欢迎随时联系我。 开发语言&#xff1a; Java 数据库&#xff1a; MySQL 技术&#xff1a; JSP技术 Java语言 工具&#xff1a; MyEclipse、Tomcat服…

华为Mate 70系列,将首发搭载纯血鸿蒙正式版,第四季度登场

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 更多资源欢迎关注 6月22日消息&#xff0c;华为在HDC 2024上已经宣布&#xff0c;HarmonyOS NEXT开启开发者先锋用户Beta测试。 首批覆盖Mate 60系列、Mate X5系列、MatePad Pro 13.2英寸。 根据官方公布的时间表&…

oracle发送https请求

参照 https://docs.oracle.com/cd/E11882_01/appdev.112/e40758/u_http.htm#i1025869 https://docs.oracle.com/cd/E11882_01/network.112/e40393/asowalet.htm#ASOAG160 https://docs.oracle.com/cd/E11882_01/appdev.112/e40758/d_networkacl_adm.htm#ARPLS148 https://d…

江协科技51单片机学习- p11 Proteus安装模拟51单片机

前言&#xff1a; 本文是根据哔哩哔哩网站上“江协科技51单片机”视频的学习笔记&#xff0c;在这里会记录下江协科技51单片机开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了江协科技51单片机教学视频和链接中的内容。 引用&#xff1a; Proteus快速入门&…

OneNote for Windows 10 下载

OneNote for Windows 10 安装 1.在浏览器中输入地址&#xff1a;https://apps.microsoft.com/detail/9wzdncrfhvjl?hlzh-cn&glUS2OneNote for Windows 10 - 在 Windows 上免费下载并安装 |Microsoft StoreOneNote 是用于在设备上捕获和组织你的一切内容的数字笔记本。快速…

打破数据分析壁垒:SPSS复习必备(六)

一、数据的报表呈现 1.报表概述 (1).SPSS中的报表功能 1&#xff09;Base 模块 2&#xff09;Custom Tables 模块 3) Original Tables 模块 (2).报表的基本绘制步骤 步骤一:确定基本结构 步骤二:使用对话框绘制表格的基本结构 步骤三:完善细节 步骤四:添加其余变…

java课程设计GUI学生信息管理系统

目录 系统内容.. 3 用户界面模块... 4 数据存储模块... 4 信息管理模块... 4 管理模块.. 4 主要模块的算法描述... 4 –简要的语言描述... 4 运行及调试分析&#xff08;测试数据及测试结果&#xff09;.. 5 课程设计总结... 7 参考文献&#xff08;至少三个&#xf…

基于CSDN的Markdown文本编辑器的博客界面优化 | HTML | 文本标签 | 图像标签 | 个人主页引导

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 今天毛毛张分享的内容是如何在CSDN的Markdown编辑器中实现上图的效果&#xff0c;如果觉得能帮助到你的话就点击个人主页点点关注吧❗ 文章目录 1.前言2.基础知识3.字…

Sword and Shield Animations(劈砍防御剑盾带动画动作)

这是一个动画资产包,为剑和盾牌用户提供手工制作的成对动画和空闲。包括8向步行和跑步动画、攻击、跳跃、冲刺、向下状态移动和过渡、躲避、阻挡、蹒跚、各种配对终结者动画等。 一切你需要把剑和盾牌战士带到生活中。 动画总数:115 攻击19 区块5 关闭状态14 Evade 5 怠速9 跳…

Sequelize入门及简单的增删改查

前言 学习一下NodeJS怎么使用Sequelize怎么查询数据库数据 一、Sequelize是什么&#xff1f; Sequelize 是一个基于 promise 的 Node.js ORM, 二、搭建项目 1.安装过程 npm i -g sequelize-cli //全局安装sequelize-clinpm i sequelize mysql2 //安装sequelize和mysql2…

AI视频教程下载-与ChatGPT结合的UX用户体验/UI用户界面设计策略

Revolutionize UX_UI_ AI-Design Strategies with ChatGPT 提升你的设计工具包&#xff1a;使用ChatGPT、Figma和Miro的AI驱动UX/UI策略 50个创新UX提示 了解人工智能的基础知识。介绍ChatGPT及其底层技术。区分不同AI模型及其在设计中的应用。将AI工具融入设计工作流程的策略…