简单算法题我重拳出击 | leetcode.[面试题 17.16]按摩师-JAVA

317 阅读3分钟

这是我参与更文挑战的第5天,活动详情查看: 更文挑战

原题

面试题 17.16. 按摩师

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

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

重拳出击

懂得都懂,这题是动态规划,最关键的是他是简单题😘,所以还是按照基本套路来。

首先仔细看了看题目,看上去很像那种中等难度题打家劫舍,但是他是中等题,就暂且不研究他了。

  • 边界条件

    题目啥边界也没说,那最基本的那个nums[i]数组不能是空的,甚至连默认值都没写,但是按照我的惯例就当他是零了。根据题目意思技师同志每次做工之后要不能连续做工,所以当nums[i]的长度小于等于2的时候,基本可以简单确定。所以有:

    {0 n<1nums[0] n<2MAX(nums[0],nums[1]) n<3\left\{ \begin{aligned} 0 & &\ n < 1 \\ nums[0] & &\ n < 2 \\ MAX(nums[0],nums[1]) & &\ n < 3 \\ \end{aligned} \right.
  • 子问题

    假如一共有 n 位客人,一般动态规划题目都是自顶而下去思考。对于最后一客人,技师同志有两种选择:

    1. 给第n位客人做工,那所消耗的总时间就是 nums[n-1] 加之前的做工耗时总和
    2. 不给第n位客人做工,那所消耗的时间就是第 n-1 位客人之前的做工耗时总和

    上面的这两种情况可以一直往下去递推,所以我们可以这样设置子问题:dp[n-1]为给第n位客人做工所消耗的总时间

  • 方程

    根据上一条总结的子问题,由于技师每次做工之后都需要休息一会,不能使用连续的顾客。所以上面的子问题就可以再次细分:

    1. 给第n位客人做工之后,技师同志就只能从 n - 2 往前的顾客中去选择了,这时候的问题又可以继续套娃
    2. 不给第n位客人做工,技师同志就只能从 n - 1 往前的顾客中去选择了,这时候前面的情况一样可以套娃

    可以尝试列出来下面的状态转移方程( 数组的下标是从 0 开始的 ):

    dp(n)={0 n<1nums[0] n<2MAX(nums[0],nums[1]) n<3Max(nums[n1]+dp[n3],dp[n2]) n>=3dp(n)=\left\{ \begin{aligned} 0 & &\ n < 1 \\ nums[0] & &\ n < 2 \\ MAX(nums[0],nums[1]) & &\ n < 3 \\ Max(nums[n - 1] + dp[n - 3], dp[n - 2]) & &\ n >= 3 \\ \end{aligned} \right.
  • 结合各种条件写代码

        public int massage(int[] nums) {
            int length = nums.length;
            //基本情况判断
            if (length < 1) {
                return 0;
            } else if (length < 2) {
                return nums[0];
            } else if (length < 3) {
                return Math.max(nums[0], nums[1]);
            }
    		
            //子问题,初始化dp
            int[] dp = new int[length ];
            dp[0] = nums[0];
            dp[1] = Math.max(nums[0], nums[1]);
            //根据动态转移方程变代码
            for (int i = 3; i < length + 1; i++) {
                dp[i - 1] = Math.max(nums[i - 1] + dp[i - 3], dp[i - 2]);
            }
            return dp[length - 1];
        }
    

    然后运行,发现 **执行耗时击败了100.00% ,内存击败了92.83% **

  • 动态规范题目找出合适子问题对解题帮助真大
  • 内存消耗可以通过用几个 int 类型参数去一直替换dp数组