【魅力算法】“按摩师/打家劫舍”-动态规划、滚动数组思想实现 |刷题打卡

885 阅读3分钟

题目描述

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

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

 

示例 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。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/the-masseuse-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

动态规划

题目理解

按摩师对于每一个预约请求:

  • 可以接受预约;
  • 可以不接受预约;
  • 且不能接受相邻的预约请求;
  • 最后需要得出请求序列总预约时间最长

举例1:预约序列为 [1,2,3,1]有以下几种可能:

  • 假设我们接受1号预约,则不能接受2号预约;
    • 为了总预约时长最长;
    • 在3号/4号预约中,选择3预约;
    • 总时长为:1+3=4;
  • 假设我们不接受1号预约,则可选择接受2号/3号预约;
    • 假设选择2号预约,则必须选择4号预约:总时长为:2+1=3;
    • 假设选择3号预约,由于不接受相邻预约,则后面的预约不接受;总时长为:3 综上:最佳选择为:1+3=4;

标准动态规划问题:

  • 定义状态:dp[i]表示:序列中 0 至 i 区间,接受预约请求最大的时长;
  • 状态转移方程:
    • 接受预约:则前一天一定休息,由于状态dp[i-1]定义涵盖了i-1这一天接受预约的情况,
      • 状态转移:dp[i]的状态一定由 dp[i-2]+nums[i] 转移而来;
    • 不接受预约:则前一天可以休息,也可以不休息,
      • 状态转移:dp[i]的状态由 下标为 i-1的 dp[i-1]状态转移而来
    • 状态转移方程为:dp[i]=max(dp[i-2]+nums[i],dp[i-1]);
  • 初始化:
    • 由状态转移方程可知,下标最小为 i-2;
    • 因此需要初始化dp[0],dp[1],从dp[2]开始计算,即:第三天开始动态规划;
    • 考虑到需接受预约请求最大的时长:
      • dp[0]:只有一天的时候,必须接受预约;
        • dp[0]=nums[0];
      • dp[1]:前两天,由于不能同时接受预约,因此最优解为:
        • dp[1]=max(nums[0],nums[1]);

时间复杂度O(N):N为数组的长度;

空间复杂度O(N):状态数组的大小为N;

代码

/**
 * @param {number[]} nums
 * @return {number}
 */
var massage = function(nums) {

 let len=nums.length;
 if(len==0){
     return 0;
 }
 if(len==1){
     return nums[0]
 }
 // 定义状态:dp[i]表示:0 至 i区间,接受预约请求最大的时长;
  let dp=[len];
  dp[0]=nums[0];
  dp[1]=Math.max(nums[0],nums[1]);
  for(let i=2;i<len;i++){
      // 今天是否接受预约,选则一个时间最长的
      dp[i]=Math.max(dp[i-1],dp[i-2]+nums[i]);
  }
  return dp[len-1]
  
};

解题思路

滚动数组思想

  • 由于只涉及到dp[i]、dp[i-1]、dp[i-2];
  • 当i=2时:
    • dp[i%3]=Math.max(dp[(i-1)%3],dp[(i-2)%3]+nums[i]);
      • dp[2%3]=Math.max(dp[(2-1)%3],dp[(2-2)%3]+nums[i]);
      • dp[2]=Math.max(dp[1],dp[0]+nums[i]);
  • 使用3个变量滚动完成计算,将空间优化到常数级别;

时间复杂度O(N):N为数组的长度;

空间复杂度O(1):状态数组的大小为3;

/**
 * @param {number[]} nums
 * @return {number}
 */
var massage = function(nums) {

 let len=nums.length;
 if(len==0){
     return 0;
 }
 if(len==1){
     return nums[0]
 }
 // 定义状态:dp[i%3]表示:0 至 i区间,接受预约请求最大的时长;
  let dp=[3];
  dp[0]=nums[0];
  dp[1]=Math.max(nums[0],nums[1]);
  for(let i=2;i<len;i++){
      // 今天是否接受预约,选则一个时间最长的
      dp[i%3]=Math.max(dp[(i-1)%3],dp[(i-2)%3]+nums[i]);
  }
  return dp[(len-1)%3]
  
};