按摩师
题目链接:按摩师
方法一:设计二维状态变量
第一步:设计状态
【状态】这个词可以理解为 【记录了求解问题到了哪一个阶段】。
由于当前这一天按摩师有两种选择:(1)接预约;(2)不接预约。但根据题意,今天是否接预约,是受到昨天影响的。为了消除这种影响,我们在状态数组要设置这个维度。
dp[i][0] 表示:区间[0,i]里预约请求序列已完成,并且下标为 i 的这一天不接受预约的情况下的最大时长
dp[i][1] 表示:区间[0,i]里预约请求序列已完成,并且下标为 i 的这一天接受预约的情况下的最大时长
说明:这个定义是有前缀性质的,即当前的状态值考虑了(或者综合了)之前的相关的状态值,第2维保存了当前最优解的决策,这种通过增加维度,消除后效性的操作在【动态规划】
问题里是非常常见的。
一般情况是,只要有约束,就可以增加一个维度消除这种约束带来的影响,再具体一点说,就是把【状态】定义的清楚、准确,【状态转移方程】就容易得到了。
第二步:状态转移方程
【状态转移方程】可以理解为 【不同阶段之间的联系】
今天只和昨天的状态相关,依然是分类讨论:
-
今天不接受预约:
- 昨天不接受预约 dp[i-1][0]
- 昨天接受了预约 dp[i-1][1]
取两者的最大值
max(dp[i-1][0],dp[i-1][1]
-
今天接受预约:(昨天没有接受预约)
dp[i][1] = dp[i-1][0] + nums[i]
第三步:考虑初始化
从第2天开始,每天的状态值只与前一天有关,因此第一天就只好老老实实算了。好在不难判断
dp[0][0] = 0
dp[0][1] = nums[0]
第四步:考虑输出
由于状态值的定义是前缀性质,因此最后一天的状态值就考虑了之前所有的天数的情况。按摩师最后一天可以接受预约,也可以不接受预约,取两者的最大值。
第五步:考虑是否可以优化空间
由于今天只参考昨天的值,可以使用【滚动数组】完成,优化空间以后的代码丢失了一定的可读性,也会给编码增加一点点难度。
参考代码1:
class Solution {
public:
int massage(vector<int>& nums) {
int len = nums.size();
if(len == 0){
return 0;
}
if(len == 1){
return nums[0];
}
// dp[i][0]:区间 [0, i] 里预约请求序列已选定,并且下标为 i 的这一天不接受预约的最大时长
// dp[i][1]:区间 [0, i] 里预约请求序列已选定,并且下标为 i 的这一天接受预约的最大时长
vector<vector<int> > dp(len,vector<int>(2));
//初始化
dp[0][0] = 0;
dp[0][1] = nums[0];
for(int i=1;i<len;i++){
dp[i][0] = max(dp[i-1][0],dp[i-1][1]);
dp[i][1] = dp[i-1][0] + nums[i];
}
return max(dp[len-1][0],dp[len-1][1]);
}
};
时间复杂度:O(N),N是数组的长度
空间复杂度:O(N),状态数组的大小为2N,可以优化到常数级别。
方法二:设计一维状态变量
第一步:定义状态
dp[i]:表示区间[0,i] 里接受预约请求的最大时长
第二步:状态转移方程
这个时候因为不限定下标i 这一天是否接受预约,因此需要分类讨论:
- 接受预约,那么昨天就一定休息。由于状态dp[i-1] 的定义涵盖了下标为 i-1 这一天接收预约的情况,状态只能从下标为 i-2 的状态转移而来:dp[i-2] + nums[i]
- 不接收预约,不用考虑昨天是否休息,状态从下标为i-1 的状态转移过来 dp[i-1]
二者取最大值,因此状态转移方程为 dp[i] = max(dp[i-1],dp[i-2] + nums[i])。
第三步:思考初始化
看状态转移方程,下标最小到 i- 2,因此初始化的时候要把dp[0],dp[1]算出来,从dp[2]开始计算。
- dp[0]:只有一天的时候,必须接受预约,因此dp[0] = nums[0];
- dp[1]:头两天的时候,由于不能同时接收预约,因此最优值就是这两天的最大值 dp[1] = max(dp[0],dp[1])
第四步:思考输出
由于定义的状态有前缀性质,并且对于下标为 i 的这一天也考虑了接收预约与不接收预约的情况,因此输出就是最后一天的状态值
第五步:思考空间优化
看状态转移方程。当前状态只与前两个状态有关,我们只关心最后一天的状态值。因此依然可以使用【滚动变量】的技巧,这个时候滚动起来的就是 3 个变量了。这样的代码依然是丢失了可读性,
也存在一定编码错误的风险。
参考代码2:
class Solution {
public:
int massage(vector<int>& nums) {
int len = nums.size();
if(len == 0){
return 0;
}else if(len == 1){
return nums[0];
}else if(len == 2){
return max(nums[0],nums[1]);
}
vector<int> dp(len);
dp[0] = nums[0];
dp[1] = max(nums[0],nums[1]);
for(int i = 2;i<len;i++){
dp[i] = max(dp[i-1],dp[i-2] + nums[i]);
}
return dp[len-1];
}
};