題目來源:力扣
題目介紹:
一個有名的按摩師會收到源源不斷的預約請求,每個預約都可以選擇接或不接。在每次預約服務之間要有休息時間,因此她不能接受相鄰的預約。給定一個預約請求序列,替按摩師找到最優的預約集合(總預約時間最長),返回總的分鐘數。
注意:本題相對原題稍作改動
=============================================================
示例 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];
}
}