面试题 17.16 按摩师
链接:https://leetcode-cn.com/problems/the-masseuse-lcci/
一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。
输入: [1,2,3,1]
输出: 4
解释: 选择 1 号预约和 3 号预约,总时长 = 1 + 3 = 4。
输入: [2,7,9,3,1]
输出: 12
解释: 选择 1 号预约、 3 号预约和 5 号预约,总时长 = 2 + 9 + 1 = 12。
输入: [2,1,4,5,3,1,1,3]
输出: 12
解释: 选择 1 号预约、 3 号预约、 5 号预约和 8 号预约,总时长 = 2 + 4 + 3 + 3 = 12。
解法1:动态规划
我的思路是,用dp[i]
表示第i
个预约的话能获得的最大预约时长,最后要求的是dp[i]
与dp[i - 1]
中的较大者(因为这两个都有可能是满足条件的)
然后找出状态转移方程,因为需要间隔预约,所以如果预约了dp[i]
,那么dp[i - 1]
是一定不能预约的,要比较的就是dp[i - 2]
和dp[i - 3]
的大小,至于dp[i - 4]
不需要比较了,因为它月dp[i - 2]
又相隔了一次预约,可以累加到dp[i - 2]
中了
然后需要给出初始值,dp[0]
只能等于nums[0]
,dp[1]
应该是nums[0]
和nums[1]
的较大者,dp[2]
应该是nums[0] + nums[2]
和nums[1]
的较大者
var massage = function (nums) {
const length = nums.length;
if (nums.length === 0) {
return 0;
}
if (nums.length === 1) {
return nums[0];
}
if (nums.length === 2) {
return Math.max(nums[0], nums[1]);
}
if (nums.length === 3) {
return Math.max(nums[0] + nums[2], nums[1]);
}
let dp = [nums[0], Math.max(nums[0], nums[1]), Math.max(nums[0] + nums[2], nums[1])];
for (let i = 3; i < length; i++) {
dp[i] = Math.max(dp[i - 2], dp[i - 3]) + nums[i];
}
return Math.max(dp[length - 1], dp[length - 2]);
};
执行用时:76ms, 在所有JavaScript提交中击败了90.05%的用户,内存消耗:37.6MB, 在所有JavaScript提交中击败了87.34%的用户
解法2:动态规划
其实本质上仍然是上一种思路,只不过换了一种思考方式,更容易理解一些(为什么我想到的思路都是乱七八糟的)
因为是间隔预约,所以要比较的是dp[i- 1]
与dp[i - 2] + nums[i]
的较大者
var massage = function (nums) {
const length = nums.length;
if (nums.length === 0) {
return 0;
}
if (nums.length === 1) {
return nums[0];
}
if (nums.length === 2) {
return Math.max(nums[0], nums[1]);
}
let dp = [nums[0], Math.max(nums[0], nums[1])];
for (let i = 2; i < length; i++) {
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
}
return dp[length - 1];
};