动态规划-简单多状态dp问题 -- 按摩师
文章目录
题目重现
题目链接:按摩师 - 力扣
一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接 。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约 。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。
示例 1:输入: [1,2,3,1] 输出: 4 解释: 选择 1 号预约和 3 号预约,总时长 = 1 + 3 = 4。
示例 2:
输入: [2,7,9,3,1] 输出: 12 解释: 选择 1 号预约、 3 号预约和 5 号预约,总时长 = 2 + 9 + 1 = 12。
示例 3:
输入: [2,1,4,5,3,1,1,3] 输出: 12 解释: 选择 1 号预约、 3 号预约、 5 号预约和 8 号预约,总时长 = 2 + 4 + 3 + 3 = 12。
算法流程
1.状态表示
这里我们选用较为常用的方式:以某个位置为结尾,定义一个状态表示:
dp[i] 表示:选择到 i 位置时,此时的最长预约时长
但是该题中我们对于每个 i 位置都有 "选择" 和 "不选择" 两种方案,所以对 dp[i] 进行细分为 f[i] 和 g[i] :
- f[i] 表示:选择到 i 位置时,nums[i] 被选择,此时的最长预约时长
- g[i] 表示:选择到 i 位置时,nums[i] 不被选,此时的最长预约时长
2.状态转移方程
由于往常状态表示只有一个,状态转移方程也只有一个,但是如今有两个状态表示 f 和 g,所以状态转移方程应当也有两个:
对于 f[i]:
- 如果 nums[i] 被选择,那么 nums[i - 1] 就必然没有被选择,因为被选中的元素所在位置不能是相邻的。那么 g[i - 1] 刚好表示 i - 1 位置未被选择情况下,截止 i - 1 位置最长的预约时长。从而该位置被选择的情况下,相应的 f[i] = g[i - 1] + nums[i]
对于 g[i]:
- 如果 nums[i] 没有被选择,那么 nums[i - 1] 就存在两种可能,即被选和未被选两种。对于 i 位置而言,仅考虑 i - 1 位置对应的最长时长,而不是 i - 1 位置是否被选中,所以 g[i] = max(f[i - 1], g[i - 1])
3.初始化
既然我们已经得到对于每个 i 位置的状态转移方程,那么我们就不难看出每个 i 节点的状态 f[i] 和 g[i] 都离不开 i - 1 位置的状态值。所以我们结合题意和状态表示,给出 i = 0 位置的初始化:
- f[0] = nums[0]
- g[0] = 0
4.填表顺序
通过 < 3.初始化 > 中的描述得知,填表顺序为:
从左往右,两表同填
5.返回值
根据状态表示,i 位置对应的值即为最长预约时间,又因为其有两种状态(被选择和未被选),所以返回值为:
max(f[n - 1], g[n - 1])
示例代码
cpp
class Solution {
public:
int massage(vector<int>& nums) {
int n = nums.size();
if(n == 0) { return 0; }
vector<int> f(n); // 选nums[i], 最大分钟数
auto g = f; // 不选nums[i], 最大分钟数
f[0] = nums[0];
g[0] = 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(f[n - 1], g[n - 1]);
}
};
提交结果: