leetcode
leetcode 2851 ~ 2900
按摩师

按摩师

难度:

标签:

题目描述

A popular masseuse receives a sequence of back-to-back appointment requests and is debating which ones to accept. She needs a break between appointments and therefore she cannot accept any adjacent requests. Given a sequence of back-to-back appoint­ ment requests, find the optimal (highest total booked minutes) set the masseuse can honor. Return the number of minutes.

Note: This problem is slightly different from the original one in the book.

 

Example 1:

Input:  [1,2,3,1]
Output:  4
Explanation:  Accept request 1 and 3, total minutes = 1 + 3 = 4

Example 2:

Input:  [2,7,9,3,1]
Output:  12
Explanation:  Accept request 1, 3 and 5, total minutes = 2 + 9 + 1 = 12

Example 3:

Input:  [2,1,4,5,3,1,1,3]
Output:  12
Explanation:  Accept request 1, 3, 5 and 8, total minutes = 2 + 4 + 3 + 3 = 12

代码结果

运行时间: 24 ms, 内存: 16.1 MB


/*
 * 思路:
 * 由于 Java Stream 不适合直接处理动态规划问题,这里采用传统方法计算后输出结果。
 * 1. 使用动态规划解决问题,计算出最长预约时间。
 * 2. 用 Stream 对结果进行格式化输出。
 */
import java.util.stream.IntStream;

public class Solution {
    public int massage(int[] nums) {
        if (nums.length == 0) return 0;
        if (nums.length == 1) return nums[0];
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        dp[1] = Math.max(nums[0], nums[1]);
        for (int i = 2; i < nums.length; i++) {
            dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i]);
        }
        return dp[nums.length - 1];
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        int[] nums = {2, 7, 9, 3, 1};
        System.out.println(IntStream.of(solution.massage(nums)).mapToObj(String::valueOf).findFirst().orElse(""));
    }
}

解释

方法:

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

时间复杂度:

O(n^2)

空间复杂度:

O(n)

代码细节讲解

🦆
在算法描述中提到的状态转移方程dp[i] = max(dp[0], dp[1], ..., dp[i-2]) + nums[i]的逻辑是如何确保不选择相邻预约的?
状态转移方程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数组为nums的副本是为了简化dp数组的初始条件设置,使得每个dp[i]的初始值即为单独选择第i个预约时的时长。这种初始化方式直接映射了每个预约的时长至dp数组,便于后续的状态转移操作。除此之外,也可以选择初始化dp数组为全0,并单独设置dp[0]和dp[1](如果存在)的值,这种方法同样可以处理所有情况,但需要在循环开始前特别处理边界情况。
🦆
代码中对于长度小于2的nums数组如何处理?dp数组的初始条件是否能够覆盖所有边界情况?
代码中如果数组长度小于2,特别是长度为0时直接返回0,长度为1时直接返回nums[0],这是因为在这些情况下没有必要进行复杂的动态规划计算。对于长度为2的情况,由于dp数组已经初始化为nums的副本,因而dp[0]和dp[1]已正确设置,可以直接返回max(dp[0], dp[1])。这种处理方法确保了所有边界情况都被覆盖,因为dp数组的初始化已经为每个可能的选择提供了初始最优解。

相关问题