题目来源:力扣
题目介绍:
一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。
注意:本题相对原题稍作改动
=============================================================
示例 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。=============================================================
审题:
首先介绍一下我自己的思路,该思路时间时间复杂度为,而官方题解给出的最优解法时间复杂度为.
我们记为数组a[0:n]中最佳预约序列的总时长,假设当前第k个活动为最佳预约序列的第一个预约.我们可以推导出如下最优子结构:,其中T[k]为k号预约的时长.若当前共有n个活动,则.我们不难发现各子问题间存在重叠部分,因此我们可以使用动态规划算法解决该问题.基于以上最优子结构设计,我们的算法中需要解决的子问题个数共有O(N)个,而每一子问题的选择有O(N)个,因此该算法的时间复杂度为.
从另一个角度构建最优子结构,即为数组[0,k]的最长预约时间,当前可能由两种情况转移而来,选择第k个预约活动与不选择第k个预约活动:
1)如果选择第k个预约活动,则
2)如果不选择第k个预约活动,则
因此,
基于以上最优子结构,我们的算法需要解决的子问题为O(N),每一子问题的选择为O(1),因此该算法的时间复杂度为O(N).
由于当前活动最多仅与前其两步活动有关,因此我们在计算S[k]时,无需知道S[k-2],…S[0],基于此,我们不需要使用O(N)的空间复杂度,我们仅需使用大小为3的数组保存当前最近3个活动值即可.
java算法:
时间复杂度
class Solution {
public int massage(int[] nums) {
if(nums.length == 0)
return 0;
int[] S = new int[nums.length+2];
S[nums.length+1] = 0;
S[nums.length] = 0;
S[nums.length-1] = nums[nums.length-1];
for(int i = nums.length-2; i >= 0; i--){
for(int j = i; j < nums.length; j++){
S[i] = Math.max(S[i], nums[j] + S[j+2]);
}
}
return S[0];
}
}
时间复杂度
class Solution {
//S[i]表示数组[0, i]的最长预约时间
//S[i]可能包含两种情况, 第i个活动预约,第i个活动不预约
//如果第i个活动预约, 则S[i] = S[i-2] + T[i]
//如果第i个活动不预约, 则S[i] = S[i-1]
//其中S[0] = T[0]
//S[1] = max{T[0], T[1]}
public int massage(int[] nums) {
if(nums.length == 0)
return 0;
if(nums.length == 1)
return nums[0];
int[] S = new int[nums.length];
S[0] = nums[0];
S[1] = Math.max(nums[0], nums[1]);
for(int i = 2; i < nums.length; i++)
S[i] = Math.max(S[i-2]+nums[i], S[i-1]);
return S[S.length-1];
}
}
时间复杂度,空间复杂度
class Solution {
//S[i]表示数组[0, i]的最长预约时间
//S[i]可能包含两种情况, 第i个活动预约,第i个活动不预约
//如果第i个活动预约, 则S[i] = S[i-2] + T[i]
//如果第i个活动不预约, 则S[i] = S[i-1]
//其中S[0] = T[0]
//S[1] = max{T[0], T[1]}
public int massage(int[] nums) {
if(nums.length == 0)
return 0;
if(nums.length == 1)
return nums[0];
int[] S = new int[3];
S[0] = nums[0];
S[1] = Math.max(nums[0], nums[1]);
for(int i = 2; i < nums.length; i++)
S[i%3] = Math.max(S[(i-2)%3]+nums[i], S[(i-1)%3]);
return S[(nums.length-1) % 3];
}
}