【动态规划】速解简单多状态类问题

embedded/2024/9/24 13:16:53/

目录

17.16 按摩师

题⽬描述:

解法(动态规划):

1. 状态表⽰:

2. 状态转移⽅程:

3. 初始化:

4. 填表顺序

5. 返回值

代码

总结:

213.打家劫舍II(medium)

 题⽬描述:

 解法(动态规划

代码:

740.删除并获得点数

题⽬描述:

 解法(动态规划):

代码:

剑指OfferI I091. 粉刷房⼦

题⽬描述:

解题思路:

代码:


17.16 按摩师

 //打家劫舍问题的变形~⼩偷变成了按摩师

题⽬描述:

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

解法(动态规划):

算法思路:

1. 状态表⽰:

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

  • i. 以某个位置为结尾,巴拉巴拉;
  • ii. 以某个位置为起点,巴拉巴拉。

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

dp[i] 表⽰:选择到 i 位置时,此时的最⻓预约时⻓。

 但是我们这个题在 i 位置的时候,会⾯临「选择」或者「不选择」两种抉择,所依赖的状态需要 细分:

▪ f[i] 表⽰:选择到 i 位置时, nums[i] 必选,此时的最⻓预约时⻓;

▪ g[i] 表⽰:选择到 i 位置时, nums[i] 不选,此时的最⻓预约时⻓。

2. 状态转移⽅程:

因为状态表⽰定义了两个,因此我们的状态转移⽅程也要分析两个:

相当于是有 选和不选两种状态

对于f[i] :

▪ 如果nums[i] 必选,那么我们仅需知道i - 1 位置在不选的情况下的最⻓预约时⻓, 然后加上nums[i] 即可,因此f[i] = g[i - 1] + nums[i] 。

对于g[i] :

 ▪ 如果nums[i] 不选,那么i - 1 位置上选或者不选都可以。因此,我们需要知道i - 1 位置上选或者不选两种情况下的最⻓时⻓,因此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 massage(vector<int>& nums) {int n = nums.size();if (n == 0) {return 0;}//要记得特例情况的判断vector<int> f(n);auto g = f;f[0] = nums[0];//初始化for (int i = 1; i < n; i++) {f[i] = g[i - 1] + nums[i];g[i] = max(f[i - 1], g[i - 1]);}return max(g[n - 1], f[n - 1]);}
};

总结:

213.打家劫舍II(medium)

 题⽬描述:

你是⼀个专业的⼩偷,计划偷窃沿街的房屋,每间房内都藏有⼀定的现⾦。这个地⽅所有的房屋都围成⼀圈,这意味着第⼀个房屋和最后⼀个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防 盗系统,如果两间相邻的房屋在同⼀晚上被⼩偷闯⼊,系统会⾃动报警。给定⼀个代表每个房屋存放⾦额的⾮负整数数组,计算你在不触动警报装置的情况下,今晚能够偷 窃到的最⾼⾦额。

//理解:就是不能两个房间连着偷,就还是f or g 的问题啦

 解法(动态规划

算法思路: 这⼀个问题是「打家劫舍I」问题的变形。

上⼀个问题是⼀个「单排」的模式,这⼀个问题是⼀个「环形」的模式,也就是⾸尾是相连的。但 是我们可以将「环形」问题转化为「两个单排」问题

a. 偷第⼀个房屋时的最⼤⾦额x ,此时不能偷最后⼀个房⼦,因此就是偷[0, n - 2] 区间 的房⼦; b. 不偷第⼀个房屋时的最⼤⾦额y ,此时可以偷最后⼀个房⼦,因此就是偷[1, n - 1] 区 间的房⼦;

两种情况下的「最⼤值」,就是最终的结果。

因此,问题就转化成求「两次单排结果的最大值」。 

代码:

class Solution {
public:int rob(vector<int>& nums) {int n = nums.size();// 两种情况下的最⼤值return max(nums[0] + rob1(nums, 2, n - 2), rob1(nums, 1, n - 1));}int rob1(vector<int>& nums, int left, int right) {if (left > right)return 0;// 1. 创建 dp 表// 2. 初始化// 3. 填表// 4. 返回结果int n = nums.size();vector<int> f(n);auto g = f;f[left] = nums[left]; // 初始化for (int i = left + 1; i <= right; i++) {f[i] = g[i - 1] + nums[i];g[i] = max(f[i - 1], g[i - 1]);}return max(f[right], g[right]);}
};

740.删除并获得点数

题⽬描述:

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

 解法(动态规划):

算法思路: 其实这道题依旧是「打家劫舍I」问题的变型。

我们注意到题⽬描述,选择x 数字的时候, x - 1 与x + 1 是不能被选择的。像不像「打家 劫舍」问题中,选择i 位置的⾦额之后,就不能选择i - 1 位置以及i + 1 位置的⾦额呢~

因此,我们可以创建⼀个⼤⼩为10001 (根据题⽬的数据范围)的 hash 数组,将nums 数组中每⼀个元素x ,累加到hash 数组下标为x 的位置处,然后在hash 数组上来⼀次「打 家劫舍」即可。

代码:

class Solution {
public:int deleteAndEarn(vector<int>& nums) {const int N = 10001;// 1. 预处理//把没见过的往见过的上面靠int arr[N] = {0};for (auto x : nums)arr[x] += x;//记录重复数字的和// 2. 在 arr 数组上,做⼀次 “打家劫舍” 问题// 创建 dp 表vector<int> f(N);auto g = f;// 填表for (int i = 1; i < N; i++) {f[i] = g[i - 1] + arr[i];g[i] = max(f[i - 1], g[i - 1]);}// 返回结果return max(f[N - 1], g[N - 1]);}
};

 预处理:把没见过的往见过的上面靠~

剑指OfferI I091. 粉刷房⼦

题⽬描述:

假如有一排房子,共 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。

示例 2:

输入: costs = [[7,6,2]]
输出: 2

解题思路:

代码:

class Solution {
public:int minCost(vector<vector<int>>& costs){int minCost(vector<vector<int>> & costs){// dp[i][j] 第i个房⼦刷成第j种颜⾊最⼩花费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]));//只能两个一min
}
}
;

sum:

1.情况选择的表示:

两种:f or g

三种:二维数组

2.然后分别写出情况下的dp方程


http://www.ppmy.cn/embedded/46699.html

相关文章

多模块工程中Controller中注入Service报错的问题

问题 2024-06-05 22:05:12,241 ERROR [http-nio-8888-exec-1][DirectJDKLog.java:175] - Servlet.service() for servlet [dispatcherServlet] in context with path [/content] threw exception [Request processing failed; nested exception is org.apache.ibatis.binding.…

植物大战僵尸杂交版2.0.88最新版+防闪退工具V2+修改工具+高清工具

植物大战僵尸杂交版&#xff0c;不仅继承原作的经典玩法&#xff0c;而且引入了全新的植物融合玩法&#xff0c;将各式各样的植物进行巧妙的杂交&#xff0c;孕育出前所未有、功能各异的全新植物。 创新的杂交合成系统 游戏引入了创新的杂交合成系统&#xff0c;让玩家可以将不…

深圳python后端面试(20240528)

深圳python后端面试&#xff08;20240528&#xff09; 面试前面试中面试后 面试前 HR:和您约了今天10.30的面试哦&#xff0c;请注意安排时间。 我&#xff1a;好的&#xff0c;已经在地铁上。 我&#xff1a;您好&#xff0c;请问是XXX吗&#xff0c;我到楼下了&#xff0c;巴…

从0开始读C++Primer|第一章 开始

1.编写一个简单的C程序 组成&#xff1a; C程序由多个函数组成&#xff0c;其中一个必须为mian函数。那么我们就有必要了解函数的组成。函数的组成&#xff1a;函数返回类型、函数名、形参列表、函数体。我感觉自己在平时经常忘记写形参和返回值&#xff0c;其实还是没有搞懂函…

kafka-消费者组-点对点测试

文章目录 1、点对点测试1.1、获取 kafka-consumer-groups.sh 的帮助信息1.2、列出所有的 消费者组1.3、创建消费者1并指定组 my_group11.4、创建消费者2并指定组 my_group11.5、创建消费者3并指定组 my_group11.6、创建生产者发送消息到 my_topic1 主题1.6.1、发送第一条消息ro…

线程池的使用

线程池 一、Java线程池介绍 在Java中&#xff0c;线程池是一种管理和复用线程的机制&#xff0c;用于提高多线程应用程序的性能和资源利用率。线程池在执行任务时&#xff0c;可以避免频繁地创建和销毁线程&#xff0c;从而减少了系统开销&#xff0c;并且能够更有效地利用系统…

arco design表单label和输入框的空间分布

表单空间分布 arco利用的栅格系统来实现label、input的大小分布 <a-form :model"formData.form" :label-col-props"{ span: 6 }" :wrapper-col-props"{ span: 18 }" >// 其它...... </a-form>栅格系统中&#xff0c;默认空间总量2…

智慧冶金:TSINGSEE青犀AI+视频技术助力打造高效、安全的生产环境

一、建设背景 冶金行业因其特殊的生产环境和工艺要求&#xff0c;对安全生产、环境保护以及质量监控等方面有着极高的要求。因此&#xff0c;将视频智能监控技术引入冶金行业&#xff0c;不仅有助于提升生产效率&#xff0c;更能有效保障生产安全&#xff0c;降低事故风险。 …