按摩师

标签: 数组 动态规划

难度: Easy

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

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

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

Submission

运行时间: 24 ms

内存: 16.1 MB

class Solution:
    def massage(self, nums: List[int]) -> int:
        if not nums:
            return 0
        dp = nums[:]
        for i in range(2, len(nums)):
            dp[i] = max(dp[:i-1]) + nums[i]
        return max(dp)

Explain

动态规划。定义dp数组,其中dp[i]表示考虑前i个预约时的最大总时长。状态转移方程为dp[i] = max(dp[0], dp[1], ..., dp[i-2]) + nums[i],即当前预约的最大总时长等于前i-2个预约的最大总时长加上当前预约的时长。

时间复杂度: O(n^2)

空间复杂度: O(n)

class Solution:
    def massage(self, nums: List[int]) -> int:
        if not nums:
            return 0
        dp = nums[:]
        for i in range(2, len(nums)):
            dp[i] = max(dp[:i-1]) + nums[i]  # 更新dp[i]为前i-2个预约的最大总时长加上当前预约的时长
        return max(dp)  # 返回最大总时长

Explore

状态转移方程dp[i] = max(dp[0], dp[1], ..., dp[i-2]) + nums[i]通过仅考虑到dp[i-2]的值来更新dp[i],从而确保了不会选择相邻的预约。由于dp[i]的值是基于i-2及之前的预约的最大总时长(即所有不包括i-1的情况)加上当前的预约时长nums[i]来计算的,这自然排除了与i直接相邻的i-1预约,保证了选择的预约不相邻。

初始化dp数组为nums的副本是为了简化dp数组的初始条件设置,使得每个dp[i]的初始值即为单独选择第i个预约时的时长。这种初始化方式直接映射了每个预约的时长至dp数组,便于后续的状态转移操作。除此之外,也可以选择初始化dp数组为全0,并单独设置dp[0]和dp[1](如果存在)的值,这种方法同样可以处理所有情况,但需要在循环开始前特别处理边界情况。

代码中如果数组长度小于2,特别是长度为0时直接返回0,长度为1时直接返回nums[0],这是因为在这些情况下没有必要进行复杂的动态规划计算。对于长度为2的情况,由于dp数组已经初始化为nums的副本,因而dp[0]和dp[1]已正确设置,可以直接返回max(dp[0], dp[1])。这种处理方法确保了所有边界情况都被覆盖,因为dp数组的初始化已经为每个可能的选择提供了初始最优解。