4.4 KiB
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:
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:
**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:
**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, 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:
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:
Stage 4:
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;
}
};