177 lines
4.3 KiB
Markdown
177 lines
4.3 KiB
Markdown
# Leetcode House-Robber
|
|
|
|
#### 2022-07-20 22:21
|
|
|
|
> ##### Algorithms:
|
|
> #algorithm #dynamic_programming
|
|
> ##### Difficulty:
|
|
> #coding_problem #difficulty-medium
|
|
> ##### Additional tags:
|
|
> #leetcode
|
|
> ##### Revisions:
|
|
> N/A
|
|
|
|
##### Related topics:
|
|
##### Links:
|
|
- [Link to problem](https://leetcode.com/problems/house-robber/)
|
|
- [Tutorial and explanation on DP](https://leetcode.com/problems/house-robber/discuss/156523/From-good-to-great.-How-to-approach-most-of-DP-problems.)
|
|
___
|
|
### Problem
|
|
|
|
You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security systems connected and **it will automatically contact the police if two adjacent houses were broken into on the same night**.
|
|
|
|
Given an integer array `nums` representing the amount of money of each house, return _the maximum amount of money you can rob tonight **without alerting the police**_.
|
|
|
|
#### Examples
|
|
|
|
**Example 1:**
|
|
|
|
```markdown
|
|
**Input:** nums = [1,2,3,1]
|
|
**Output:** 4
|
|
**Explanation:** Rob house 1 (money = 1) and then rob house 3 (money = 3).
|
|
Total amount you can rob = 1 + 3 = 4.
|
|
```
|
|
|
|
**Example 2:**
|
|
|
|
```markdown
|
|
**Input:** nums = [2,7,9,3,1]
|
|
**Output:** 12
|
|
**Explanation:** Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1).
|
|
Total amount you can rob = 2 + 9 + 1 = 12.
|
|
```
|
|
|
|
#### Constraints
|
|
|
|
- `1 <= nums.length <= 100`
|
|
- `0 <= nums[i] <= 400`
|
|
|
|
### Thoughts
|
|
|
|
> [!summary]
|
|
> This is a #dynamic_programming problem.
|
|
|
|
According to [This tuturial](https://leetcode.com/problems/house-robber/discuss/156523/From-good-to-great.-How-to-approach-most-of-DP-problems.), the code can be derived from 3 stages:
|
|
|
|
#### Stage 1: Find recursive (unoptimized) solution
|
|
|
|
The robbing problem can be simplified as follows:
|
|
|
|
> Whether to rob or not?
|
|
> - if rob, profit = nums[n] + rob(nums, n - 2)
|
|
> - else, profit = rob(nums, n - 1)
|
|
|
|
The recursive solution can be represented as follows:
|
|
|
|
##### Base case: nums.size() = 0 || 1 || 2
|
|
|
|
##### Pseudo code:
|
|
|
|
- check for base case
|
|
- return max(robbery(nums, n - 1), robbery(nums, n - 2) + nums[n])
|
|
|
|
#### Stage 2: Find recursive solution with cache
|
|
|
|
Since the core of DP as about re-using answers from before, we can cache the answers to make the code faster.
|
|
|
|
##### Pseudo code:
|
|
|
|
- Check for base case
|
|
- if cache found, return
|
|
- assign the computed value to cache
|
|
- return the cached value
|
|
|
|
#### Stage 3: Find iterative solution with caching
|
|
|
|
By using iterative, we can use less memory than recursion (return stack).
|
|
|
|
We start from bottom to top, rather than from top to down, and use the cached answer to proceed to the final answer
|
|
|
|
#### Stage 4: Optimize it by using variables rather than hash table based caching
|
|
|
|
This can be different from problem to problem, but for this case,
|
|
we achieve constant space complexity using variables.
|
|
|
|
### Solution
|
|
|
|
#### Stage 1:
|
|
|
|
TLE, I didn't save this one
|
|
|
|
#### Stage 2:
|
|
|
|
```cpp
|
|
class Solution {
|
|
vector<int> cache;
|
|
int robbery(vector<int> &nums, int nextLoc) {
|
|
// base cases:
|
|
// nextLoc == 0, return nums[1]
|
|
// nextLoc == 1, return nums[2]
|
|
if (nextLoc < 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (cache[nextLoc] != -1) {
|
|
return cache[nextLoc];
|
|
}
|
|
|
|
cache[nextLoc] = max(robbery(nums, nextLoc - 1),
|
|
robbery(nums, nextLoc - 2) + nums[nextLoc]);
|
|
|
|
return cache[nextLoc];
|
|
}
|
|
|
|
public:
|
|
int rob(vector<int> &nums) {
|
|
// Version one
|
|
// Recursion
|
|
// rob current store: nums[i] + rob(i - 2)
|
|
// don't rob current home: rob(i - 1)
|
|
cache = vector<int>(nums.size(), -1);
|
|
|
|
return robbery(nums, nums.size() - 1);
|
|
}
|
|
};
|
|
```
|
|
|
|
#### Stage 3:
|
|
|
|
```cpp
|
|
|
|
```
|
|
|
|
#### Stage 4:
|
|
|
|
```cpp
|
|
class Solution {
|
|
public:
|
|
int rob(vector<int> &nums) {
|
|
// iterative, from the buttom up
|
|
// We can utilize the hash map feature
|
|
|
|
int size = nums.size();
|
|
if (size == 0) {
|
|
return 0;
|
|
} else if (size == 1) {
|
|
return nums[0];
|
|
}
|
|
|
|
int prev1, prev2, next; // next: i + 1
|
|
|
|
int curLoc = 1;
|
|
prev1 = nums[0]; // i - 1
|
|
prev2 = max(nums[0], nums[1]); // i
|
|
next = prev2;
|
|
while (curLoc < size - 1) {
|
|
// cache[curLoc + 1] = max(cache[curLoc - 1] + nums[curLoc + 1] ,
|
|
// cache[curLoc]);
|
|
next = max(prev1 + nums[curLoc + 1], prev2);
|
|
prev1 = prev2;
|
|
prev2 = next;
|
|
curLoc++;
|
|
}
|
|
return next;
|
|
}
|
|
};
|
|
``` |