这是我参与更文挑战的第5天,活动详情查看: 更文挑战
原题
一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。
示例 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的时候,基本可以简单确定。所以有: -
子问题
假如一共有
n
位客人,一般动态规划题目都是自顶而下去思考。对于最后一客人,技师同志有两种选择:- 给第
n
位客人做工,那所消耗的总时间就是nums[n-1]
加之前的做工耗时总和 - 不给第
n
位客人做工,那所消耗的时间就是第n-1
位客人之前的做工耗时总和
上面的这两种情况可以一直往下去递推,所以我们可以这样设置子问题:
dp[n-1]
为给第n位客人做工所消耗的总时间 - 给第
-
方程
根据上一条总结的子问题,由于技师每次做工之后都需要休息一会,不能使用连续的顾客。所以上面的子问题就可以再次细分:
- 给第
n
位客人做工之后,技师同志就只能从n - 2
往前的顾客中去选择了,这时候的问题又可以继续套娃 - 不给第
n
位客人做工,技师同志就只能从n - 1
往前的顾客中去选择了,这时候前面的情况一样可以套娃
可以尝试列出来下面的状态转移方程( 数组的下标是从 0 开始的 ):
- 给第
-
结合各种条件写代码
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
数组