[dp5_多状态dp] 按摩师 | 打家劫舍 II | 删除并获得点数 | 粉刷房子

目录

1.面试题 17.16. 按摩师

题解

2.打家劫舍 II

题解

3.删除并获得点数

题解

4.粉刷房子

题解


一定要有这样的能力,碰到一个新题的时候,可以往之前做过的题方向靠!

打家劫舍问题模型:  不能选择相邻的两个数,并且要最终选择的数最大。
解决办法:  维护多个DP表,return 最值

  • 后面的三个问题,其实都可以理解为是第一个题目的变形,例如说
  • 对环形进行分情况,变为链形,抽离出DP 函数,使用两次
  • 进行一个预处理,再 DP
  • 两种情况变为多种情况

但是其实解决的思路都是一样的

1.面试题 17.16. 按摩师

链接: 面试题 17.16. 按摩师

一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。

注意:本题相对原题稍作改动

示例 1:

输入: [1,2,3,1]
输出: 4
解释: 选择 1 号预约和 3 号预约,总时长 = 1 + 3 = 4。

题解

  • 圆圈表示预约,每个预约可以接或不接
  • 不能接受相邻的预约

最优的预约集合(总预约时间最长),返回总的分钟数。

1.状态表示

  • 经验 + 题目要求

dp[i] 表示:选择到 i 位置的时候,此时最长预约时长。

但是 i 位置可以选或者不选,因此继续细化

  • f[i] 表示:选择到 i 位置的时候,nums[i] 必选,此时最长预约时长
  • g[i] 表示:选择到 i 位置的时候,nums[i] 不选,此时最长预约时长

因为 这个位置的设置,是和上一个位置有关的

2.状态转移方程

  • 依据 题目 所给的 相邻不能选规则
  • f[i]=g[i-1]+nums[i] //选 i
  • g[i]=max(f[i-1],g[i-1]) //不选 i

选的话 别忘记 i 位置的预约时长,然后相信 计算机就好啦

注释:

g[i] 表示 不选 i 位置,此时最长预约时长。 i 位置不选,那 i - 1 位置可选可不选,我们要找的是 0 ~ i - 1区间最长预约时长,有两种情况

  • 选 i - 1 位置 ,而 f[i - 1] 表示必选 i - 1 位置此时最长预约时长,
  • 不选 i - 1 位置,g[i - 1] 表示 不选 i - 1 位置此时最长预约时长

3.初始化

  • 填表时不越界

这里我们也可以添加虚拟节点,但是这道题初始化太简单了,因此就不要添加虚拟节点。

填f[0],g[0] 会越界,而f[0]表示必选0位置,g[0]表示不选0位置,因此

  • f[0] = nums[0] ,g[0] = 0

4.填表

  • 从左往右两个表一起填~

5.返回值

  • 返回到达最后一个位置时最长预约时长
  • max( f[n - 1],g[n - 1] )
class Solution {
public:int massage(vector<int>& nums) {//多状态dp//f  gint n=nums.size();if(n==0) return 0;if(n==1) return nums[0];//注意 边界处理vector<int> f(n,0);vector<int> g(n,0);f[0]=nums[0];//选for(int i=1;i<n;i++){f[i]=g[i-1]+nums[i];g[i]=max(g[i-1],f[i-1]);}return max(f[n-1],g[n-1]);       }
};

if(n==0) return 0;

if(n==1) return nums[0];

//注意 边界处理


2.打家劫舍 II

  • 打家劫舍:环形变种

链接: 213. 打家劫舍 II

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。

示例 1:

输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。

题解

  • 这道题相比于打家劫舍I 多了这个条件:这个地方所有的房屋都 围成一圈
  • 这意味着第一个房屋和最后一个房屋是紧挨着的,也就是说现在成了一个环路了。

同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。

⭕分类讨论:

  • 根据一个位置选or不选可以把问题从环形问题转化为线性问题

好好画下图,就可以想通啦

int rob(vector<int>& nums) {int n = nums.size();//分类讨论两种情况下最大值return max(dp(nums, 2, n-2) + nums[0], dp(nums, 1, n - 1));}

接下来是打家劫舍I的分析,和上面那道题几乎一模一样

1.状态表示

经验 + 题目要求

  • f[i] 表示:偷到 i 位置的时候,偷 nums[i] ,此时的最大金额
  • g[i] 表示:偷到 i 位置的时候,不偷 nums[i] ,此时的最大金额

2.状态转移方程

  • f[i] = g[i-1] +nums[ i]
  • g[i] =max( f[i-1],g[i-1])

3.初始化

  • 填表时不越界
  • f[0] = nums[0] ,g[0] = 0

4.填表

  • 从左往右两个表一起填

5.返回值

  • 返回偷到最后一个位置时最大金额
  • max( f[n - 1],g[n - 1] )
class Solution {
public:int n;int rob(vector<int>& nums) {n=nums.size();if(n==0) return 0;if(n==1) return nums[0];if(n==2) return nums[0]>nums[1]?nums[0]:nums[1];return max(dp(nums,1,n-1),dp(nums,2,n-2)+nums[0]);//如何 实现 对环的处理的呢?
//通过 对第一个位置的 选 不选
//将dp 隔离为一个函数,两次调用~}int dp(vector<int>& nums,int begin,int end){vector<int> f(n,0);vector<int> g(n,0);f[begin]=nums[begin];for(int i=begin+1;i<=end;i++){f[i]=g[i-1]+nums[i];g[i]=max(g[i-1],f[i-1]);}return max(f[end],g[end]);} 
};

return max(dp(nums,1),dp(nums,2)+nums[0]);

  • 如何 实现 对环的处理的呢?
  • 通过 对第一个位置的 选 不选
  • 将dp 隔离为一个函数,两次调用~

3.删除并获得点数

  • 打家劫舍:要预处理版

链接: 740. 删除并获得点数

每次操作中,选择任意一个 nums[i] ,删除它并获得 nums[i] 的点数。之后,你必须删除 所有 等于 nums[i] - 1nums[i] + 1 的元素。

开始你拥有 0 个点数。返回你能通过这些操作获得的最大点数。

示例 1:

输入:nums = [3,4,2]
输出:6
解释:
删除 4 获得 4 个点数,因此 3 也被删除。
之后,删除 2 获得 2 个点数。总共获得 6 个点数。

示例 2:

输入:nums = [2,2,3,3,3,4]
输出:9
解释:
删除 3 获得 3 个点数,接着要删除两个 2 和 4 。
之后,再次删除 3 获得 3 个点数,再次删除 3 获得 3 个点数。
总共获得 9 个点数。

题解

给你一个整数数组 nums ,你可以对它进行一些操作。

  • 每次操作中,选择任意一个 nums[i] ,删除它并获得 nums[i] 的点数。
  • 之后,你必须删除 所有 等于 nums[i] - 1 和 nums[i] + 1 的元素。
  • 开始你拥有 0 个点数。返回你能通过这些操作获得的最大点数。

对于删除它并获得 nums[i] 的点数。

  • 之后,你必须删除 所有 等于 nums[i] - 1 和 nums[i] + 1 的元素。
  • 删除 所有 等于 nums[i] - 1 和 nums[i] + 1 的元素也就是说值等于这两个的数不能选了。
  • 因此我们可以先对数组排一下序,方便找到 nums[i] - 1 和 nums[i] + 1 的 元素。

一定要有这样的能力,碰到一个新题的时候,可以往之前做过的题方向靠!

  • 比如这道题如果是有序的并且中间数没有少,就像打家劫舍的问题。
  • 比如说1、2、3、4、5。
  • 选 1 之后不能选 2 了,是不是就是不能选择相邻的两个数,并且要最终选择的数最大。

所以如果说,数组是有序的话并且中间没有间隔,就是 “打家劫舍” 问题。

  • 但是这里的数并不是那么连续的。如果中间数断了,就不是 “打家劫舍” 问题。
  • 比如这里选 2 之后,还能选 4.

所以我们可以先用数组arr,把这些数先统计一下。

  • 下标 i 表示这个数是几,arr[i] 表示 " i " 这个数出现的总和。
  • 为什么预处理到数组中,因为 下标 i 是有序的。
  • 然后就可以不要nums了,相当于在arr数组做一次 “打家劫舍” 。

比如说选了下标1里面的数,那下标 2 里面的数就不能要了,也就相当于选择当前这个数,相邻的数不能选了。可以选后面其他的数。

  • 预处理:将数组中的数,统计到 arr 中,然后在 arr 中,做一次 “打家劫舍” 问题即可

1.状态表示

  • 经验 + 题目要求
  • f[i] 表示:选到 i 位置的时候, nums[i] 必选 ,此时能获得的最大点数
  • g[i] 表示:偷到 i 位置的时候,nums[i] 不选,此时能获得的最大点数

2.状态转移方程

  • f[i]=g[i-1]+arr[i]
  • g[i]=max(f[i-1], g[i-1])

3.初始化

  • 填表时不越界
  • f[0] = arr[0] ,g[0] = 0

4.填表

  • 从左往右
  • 两个表一起填

5.返回值

  • 返回偷到最后一个位置时最大金额
  • max( f[n - 1],g[n - 1] )
class Solution {
public:int deleteAndEarn(vector<int>& nums) {sort(nums.begin(),nums.end());int n=nums.size();if(n==0) return 0;if(n==1) return nums[0];vector<int> arr(10001,0);
//预处理 建立映射for (auto num : nums) {arr[num] += num;}//dp 无需管空值,还是和以前一样就行~int m=arr.size();vector<int> f(m,0);vector<int> g(m,0);f[0]=arr[0];for(int i=1;i<m;i++){f[i]=g[i-1]+arr[i];g[i]=max(g[i-1],f[i-1]);}return max(f[m-1],g[m-1]);}
};

dp 无需管 arr 空值,还是和以前一样就行~

  • int m=arr.size();
  • vector<int> f(m,0);

4.粉刷房子

  • 打家劫舍:选择 两种变三种

链接: LCR 091. 粉刷房子

假如有一排房子,共 n 个,每个房子可以被粉刷成红色、蓝色或者绿色这三种颜色中的一种,你需要粉刷所有的房子并且使其相邻的两个房子颜色不能相同。

当然,因为市场上不同颜色油漆的价格不同,所以房子粉刷成不同颜色的花费成本也是不同的。每个房子粉刷成不同颜色的花费是以一个 n x 3 的正整数矩阵 costs 来表示的。

例如,costs[0][0] 表示第 0 号房子粉刷成红色的成本花费;costs[1][2] 表示第 1 号房子粉刷成绿色的花费,以此类推。

请计算出粉刷完所有房子最少的花费成本。

示例 1:

输入: costs = [[17,2,17],[16,16,5],[14,3,19]]
输出: 10
解释: 将 0 号房子粉刷成蓝色,1 号房子粉刷成绿色,2 号房子粉刷成蓝色。最少花费: 2 + 5 + 3 = 10。

题解

  • 有n个房子,每个房子都可以被粉刷成红色,蓝色,绿色,相邻的房子颜色不能相同。
  • 每个房子粉刷成不同颜色的价格由一个3*n的矩阵给出,左边0 ~ 2表示房子,上面0 ~ 2表示每个房子被粉刷不能颜色的价格。

要找到粉刷完所有房子最少的价格。

这不是 刚好符合 我们打家劫舍的两个模型特征了 吗~

1.状态表示

  • 经验 + 题目要求

以 i 位置为结尾,当涂到 i 位置的时候此时的最小花费,但是涂到 i 位置还可以细分,涂成红色,蓝色,绿色。

  • dp[i][0] 表示:粉刷到 i 位置的时候,最后一个位置粉刷上 红色,此时的最小花费
  • dp[i][1] 表示:粉刷到 i 位置的时候,最后一个位置粉刷上 蓝色,此时的最小花费
  • dp[i][2] 表示:粉刷到 i 位置的时候,最后一个位置粉刷上 绿色,此时的最小花费

对比 打家劫舍的两种选不选,这里是有三种选择的可能

也可以这么理解

  • f[i] == dp[i][0]
  • g[i] == dp[i][1]

所以有 四种 五种可能,我们也是这么处理


2.状态转移方程

此时分三种情况讨论:

  • 如果 i 位置涂成红色这个位置花费已经固定了 cost[i][0],我要的是最小花费,只要保证 0 ~ i -1 区间花费最小就行了。
  • 涂到 i - 1 位置只能涂成蓝色、绿色,dp[i-1][1]表示涂到 i - 1 位置涂蓝色此时最小花费,dp[i-1][2]表示涂到 i - 1 位置涂绿色此时最小花费
  • 因此 i 位置涂成红色状态转移方程就处理了,剩下也是这样的处理:

3.初始化

  • 填表时不越界

这里我们可以多申请一个空间。这样就可以把初始化放到填表里面了。但是要注意两点:

  • 虚拟节点里面的值,要保证后序填表时正确的
  • 下标的映射关系

首先看虚拟节点里面的值应该填多少

  • 可以先考虑没有虚拟节点第一个位置应该填多少,是不是填的是自己本身啊,因此虚拟节点里面的值我们给0。
  • 因为我们多开一个空间,相当于整体向右移一位,如果要回去矩阵 下标应该减 1

4.填表

  • 从左往右三个表一起填

5.返回值

  • 返回涂到最后一个位置,涂成红,蓝,绿最小花费
  • 多状态,就每个状态 都维护一个DP表min(dp[n][0],dp[n][1],dp[n][2])
class Solution {
public:int minCost(vector<vector<int>>& costs) {int n=costs.size();vector<vector<int>> dp(n+1,vector<int>(3,0));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][0],dp[i-1][1])+costs[i-1][2];//注意 下表映射}return min(dp[n][0],min(dp[n][1],dp[n][2]));//对填完了的表 ,进行选择}
};

多状态,就每个状态 都维护一个DP表

空间换时间!!!!!

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

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

相关文章

基于javaweb的SSM羽毛球会员俱乐部系统场馆课程运动设计与实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论文…

windows下git bash安装SDKMan报错Looking for unzip...Not found

需要在jdk8和jdk17两个版本切换。最简单的是通过手动切换&#xff0c;但切换过程太繁琐&#xff0c;修改环境变量&#xff0c;达到切换目的。于是尝试其它解决方案&#xff0c;最终确实使用sdkman工具。 确保安装了git Git - Downloading Package 记住安装的路径&#xff0c;…

rnn的音频降噪背后技术原理

rnniose: 这个演示展示了 RNNoise 项目&#xff0c;说明了如何将深度学习应用于噪声抑制。其核心理念是将经典的信号处理方法与深度学习结合&#xff0c;打造一个小巧、快速的实时噪声抑制算法。它不需要昂贵的 GPU —— 在树莓派上就能轻松运行。 相比传统的噪声抑制系统&…

剑指Offer(数据结构与算法面试题精讲)C++版——day3

剑指Offer&#xff08;数据结构与算法面试题精讲&#xff09;C版——day3 题目一&#xff1a;数组中和为0的3个数字题目二&#xff1a;和大于或等于k的最短子数组题目三&#xff1a;乘积小于k的子数组 题目一&#xff1a;数组中和为0的3个数字 前面我们提到&#xff0c;在一个排…

全新UI好看404页面源码

源码介绍 全新UI好看404页面源码,源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行 效果预览 源码获取 全新UI好看404页面源码

递归典例---汉诺塔

https://ybt.ssoier.cn/problem_show.php?pid1205 #include<bits/stdc.h> #define endl \n #define pii pair<int,int>using namespace std; using ll long long;void move(int n,char a,char b,char c) // n 个盘子&#xff0c;通过 b&#xff0c;从 a 移动到 …

php的高速缓存

部署方法 在我们安装的nginx中默认不支持memc和srcache功能&#xff0c;需要借助第三方模块来让nginx支持此功能。 tar zxf srcache-nginx-module-0.33.tar.gz tar zxf memc-nginx-module-0.20.tar.gz 下载这俩个模块&#xff0c;然后编译安装的时候加进去 编译安装完成之后…

视频设备轨迹回放平台EasyCVR打造视频智能融合新平台,驱动智慧机场迈向数字新时代

一、行业背景​ 随着 5G、AI、物联网、大数据等前沿技术的不断更新换代&#xff0c;交通行业进入数字化转型的高速发展时期。航空业作为交通领域的重要部分&#xff0c;数字化进程从追求速度往注重质量的转变。但机场在数字化转型中面临许多严峻挑战&#xff0c;如现有运营模式…

【论文阅读】Anchor Graph Network for Incomplete Multiview Clustering

摘要 近年来&#xff0c;不完全多视图聚类&#xff08;IMVC&#xff09;受到广泛关注。然而&#xff0c;现有研究仍然存在以下几个不足之处&#xff1a;1) 部分方法忽略了样本对在全局结构分布中的关联性&#xff1b;2) 许多方法计算成本较高&#xff0c;因此无法应用于大规模…

15. 远程服务器运行jemter的GUI方式

1. 问题 在 linux 服务器或远程服务器上&#xff0c;安装 Jmeter&#xff0c;打不开 Jmeter 的 GUI 界面。 环境&#xff1a; linux 服务器mac 电脑 需求&#xff1a;在远程服务器中&#xff0c;启动 jmeter&#xff08;./bin/jmeter &&#xff09;后&#xff0c;在 ma…

Ansible:playbook的高级用法

文章目录 1. handlers与notify2. tags组件3. playbook中使用变量3.1使用 setup 模块中变量3.2在playbook 命令行中定义变量3.3在playbook文件中定义变量3.4使用变量文件3.5主机清单文件中定义变量主机变量组&#xff08;公共&#xff09;变量 1. handlers与notify Handlers&am…

什么是msvcp140.dll?msvcp140.dll丢失的解决方法又有哪些?

msvcp140.dll 是 Microsoft Visual C Redistributable 的核心动态链接库文件&#xff0c;许多软件和游戏依赖它来运行。当系统提示“msvcp140.dll丢失”时&#xff0c;意味着该文件无法被正确加载&#xff0c;导致程序崩溃或无法启动。本文将提供最全面的 msvcp140.dll丢失的解…

(九)图形管线

一图说明问题 顶点数据->顶点着色器->细分着色器->几何着色器->光栅化->片元着色器->颜色混合 创建图形管线函数放在后面位置 void MyApplication::initVulkan() { createInstance(); createSurface(); pickPhysicalDevice(); createLogicalDevice(); cre…

《inZOI(云族裔)》50+MOD整合包

载具 RebelCore - 年龄和时间 mod启动器 优化补丁 去除雾气 坦克模型 菜单 前置 跳过启动 更好性能 等 共计50MOD整合 在游戏的世界里&#xff0c;追求更丰富、更优质的体验是玩家们永恒的主题。RebelCore 这款游戏通过精心打造的 50MOD 整合&#xff0c;为玩家带来了前所未有的…

国家天文台携手阿里云,发布国际首个太阳大模型“金乌”

2025年4月1日&#xff0c;中国科学院国家天文台与阿里云共同宣布推出全球首个太阳物理大模型“金乌”&#xff0c;在太阳活动预测领域实现颠覆性突破——其针对破坏性最强的M5级太阳耀斑预报准确率高达91%&#xff0c;远超传统数值模型&#xff0c;标志着人类对太阳的认知迈入“…

U盘实现——BOT 常用命令

文章目录 U盘实现——BOT 常用命令命令格式CBWCSW数据传输条件命令传输数据传输状态传输命令汇总INQUIRY Command:12h数据格式抓包READ FORMAT CAPACITIES Command: 23h数据格式抓包READ CAPACITY Command: 25h数据格式抓包TEST UNIT READY Command: 00h数据格式抓包WRITE(10) …

【Axure元件分享】月份范围选择器

Axure月份范围选择器是一个月份范围下拉筛选元件&#xff0c;支持月份范围定义选择。组件自动加载系统当前年月份作为默认值&#xff0c;用户可通过箭头图标或键盘快捷键快速切换年份月份&#xff0c;其样式支持高度定制&#xff0c;包括颜色主题、字体尺寸及交互反馈&#xff…

JavaScript基础-移动端常用开发框架

随着移动互联网的发展&#xff0c;越来越多的应用和服务需要支持移动设备。为了提高开发效率和用户体验&#xff0c;开发者们依赖于一些成熟的JavaScript框架来构建响应迅速、功能丰富的移动Web应用。本文将介绍几款广泛使用的移动端开发框架&#xff0c;并通过具体的示例展示它…

数字人训练数据修正和查看 不需要GPU也能运行的DH_live-加载自己训练-

自己训练模pth报错 le "D:\ai\dh_live\app.py", line 42, in demo_mini interface_mini(asset_path, wav_path, output_video_name) File "D:\ai\dh_live\demo_mini.py", line 21, in interface_mini renderModel_mini.loadModel("checkpoi…

基姆拉尔森计算公式

基姆拉尔森计算公式&#xff08;Zellers Congruence 的变体&#xff09;是一种快速根据公历日期计算星期几的数学公式。其核心思想是通过对年月日的数值进行特定变换和取模运算&#xff0c;直接得到星期几的结果。 公式定义 对于日期 年-月-日&#xff0c;公式如下&#xff1a…