LeetCode 198 House Robber
Last updated on an hour ago
❗️ 題意
給定一個陣列 $nums$,請你從裡面竟可能的選擇總和最大的數值,並且不能夠選擇相連的數字,最後請輸出能夠從此陣列中拿到的最大值為何。
Input: $[1,2,3,1]$
Output: $4$
- $nums[0] + nums[2] = 4$
Input: $[2,7,9,3,1]$
Output: $12$
- $nums[0] + nums[2] + nums[4] = 12$
Constraints:
- $1 \leq nums.length \leq 100$
- $0 \leq nums[i] \leq 400$
✔️ 題解
首先,我們可能會很直覺的想到,是否我們只需要選擇index全部為奇數或全部為偶數加起來即可,但很明顯此方法是錯的,例如 $[10, 1, 1, 10]$ ,若只選奇數或只選偶數最大皆只有 $11$ ,但若我們拿 $nums[0]$ 與 $nums[3]$ 便可以獲得 $20$ 。
因此我們可以透過類似於 Climing Stairs的方法來思考此題,假設我們要選擇房子 $i$ ,則必定保證房子 $i - 2$ 房子 $i - 3$ 一定有一個被選到,因為全部的數值皆為正數,就算有那種需要橫跨數個來選的(例如橫跨四個以上,中間的 $i - 2$ 與 $i - 3$ 一定有一個也可以被選)。因此此問題就能簡化成以下式子: $dp[i] = nums[i] + max(dp[i - 2], dp[i - 3])$
而在實作上,不管Top-Down或Button-Up的作法皆可以成功解決此問題。
💻 程式碼
Top-Down(with Memoization)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27// 2 ms, AC
class Solution {
public:
vector<int> dp;
int n = 0;
int solve(int h, vector<int>& nums){
if(h < 0){
return 0;
}
else if(h < 2){
return dp[h] = nums[h];
}
else if(dp[h] != -1){
return dp[h];
}
return dp[h] = nums[h] + max(solve(h - 2, nums), solve(h - 3, nums));
}
int rob(vector<int>& nums) {
if(nums.size() == 1){
return nums[0];
}
n = nums.size();
dp.resize(n);
fill(dp.begin(), dp.end(), -1);
return max(solve(n - 1, nums), solve(n - 2, nums));
}
};Button-Up
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// 0ms, AC
class Solution {
public:
int rob(vector<int>& nums) {
if(nums.size() == 1){
return nums[0];
}
int n = nums.size();
for(int i = 2; i < n; i++){
if(i == 2){
nums[i] += nums[i - 2];
}
else{
nums[i] += max(nums[i - 2], nums[i - 3]);
}
}
return max(nums[n - 1], nums[n - 2]);
}
};Runtime: 0 ms, faster than 100% of C++ online submissions
類似問題: 213 House Robber II
此題與 House Robber唯一的差別為其房子排序為一個環,代表第一個房子與最後一個房子也是相連的,而我們同樣可以透過類似的概念來完成此問題。
在實際上,我們可以分為兩個Cases,Case1是是不使用第一個房子,但使用最後一間(實作上直接將第一個房子設為0即可),Case2則相反,使用第一個房子而不使用最後一間,並分別對兩個Case用上面的方法求解即可
Button-Up
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28// 0ms, AC
class Solution {
public:
int rob(vector<int>& nums) {
if(nums.size() == 1){
return nums[0];
}
if(nums.size() == 2){
return max(nums[0], nums[1]);
}
int n = nums.size();
vector<int> v1 = nums;
v1[0] = 0;
vector<int> v2 = nums;
for(int i = 2; i < n; i++){
if(i == 2){
v1[i] += v1[i - 2];
v2[i] += v2[i - 2];
}
else{
v1[i] += max(v1[i - 2], v1[i - 3]);
v2[i] += max(v2[i - 2], v2[i - 3]);
}
}
return max(max(v1[n - 1], v1[n - 2]), max(v2[n - 2], v2[n - 3]));
}
};Runtime: 0 ms, faster than 100% of C++ online submissions
🧠 心得
這兩題真的蠻像的,第二題甚至只需要複製前一提的程式再稍微改一下即可!