The Cautious Burglar
TIP103 Unit 11 Session 1 (Click for link to problem statements)
The Cautious Burglar
Houses along a street hold cash amounts given by nums. A burglar cannot rob two adjacent houses on the same night without triggering an alarm.
Return the maximum amount that can be robbed without ever hitting two neighbors.
def rob(nums):
pass
Problem Highlights
- 💡 Difficulty: Medium
- ⏰ Time to complete: 20-30 mins
- 🛠️ Topics: Dynamic Programming, 1D DP, Decision Making (Rob/Skip)
1: U-nderstand
Understand what the interviewer is asking for by using test cases and questions about the problem.
- Established a set (2-3) of test cases to verify their own solution later.
- Established a set (1-2) of edge cases to verify their solution handles complexities.
- Have fully understood the problem and have no clarifying questions.
- Have you verified any Time/Space Constraints for this problem?
-
Q: What does “adjacent” mean here?
- A: Two houses that sit directly next to each other in
nums(indicesiandi + 1). The burglar may rob any set of houses as long as no two robbed houses are neighbors.
- A: Two houses that sit directly next to each other in
-
Q: Do the robbed houses have to alternate strictly (every other house)?
- A: No. The burglar can skip two or more houses in a row if that leads to a larger total; the only rule is never robbing two neighbors.
-
Q: What should be returned for an empty street?
- A: If
numsis empty, no cash can be taken, so return0.
- A: If
HAPPY CASE
Input: nums = [1, 2, 3, 1]
Output: 4
Explanation: Rob house 0 (cash = 1) and house 2 (cash = 3). They are not adjacent, and 1 + 3 = 4 is the maximum possible.
Input: nums = [2, 7, 9, 3, 1]
Output: 12
Explanation: Rob house 0 (cash = 2), house 2 (cash = 9), and house 4 (cash = 1). Total = 2 + 9 + 1 = 12.
EDGE CASE
Input: nums = []
Output: 0
Explanation: No houses means nothing to rob.
Input: nums = [1, 3, 1, 3, 100]
Output: 103
Explanation: Rob houses 1 and 4 (3 + 100). Robbing every other house starting at index 0 only yields 102, so a greedy alternating strategy fails here.
2: M-atch
Match what this problem looks like to known categories of problems, e.g. Linked List or Dynamic Programming, and strategies or patterns in those categories.
For Optimization Problems with a Rob/Skip Decision at Each Step, we can consider the following approaches:
- Dynamic Programming (1D): The best total up to house
idepends only on the best totals up to housesi - 1andi - 2, giving the recurrencebest[i] = max(best[i - 1], best[i - 2] + nums[i]). This is the classic House Robber pattern. - Recursion with Memoization: A top-down version of the same recurrence; each subproblem (best total for a prefix of houses) is solved once and cached.
3: P-lan
Plan the solution with appropriate visualizations and pseudocode.
General Idea:
Walk down the street one house at a time, tracking two running values: the best haul ending at or before the previous house (prev_one) and the best haul ending at or before the house two back (prev_two). At each house, the burglar either skips it (keeping prev_one) or robs it (adding its cash to prev_two, since the neighbor must then be untouched). Keep whichever choice is larger and slide both trackers forward.
1) Initialize prev_two = 0 and prev_one = 0.
2) For each cash amount in nums:
a) current = max(prev_one, prev_two + cash)
- prev_one -> skip this house
- prev_two + cash -> rob this house (neighbor excluded)
b) Shift the window: prev_two = prev_one, prev_one = current.
3) Return prev_one, the best total over all houses.
⚠️ Common Mistakes
- Assuming the answer is to rob every other house (strictly alternating); sometimes skipping two houses in a row is optimal, as in
[1, 3, 1, 3, 100]. - Updating
prev_twoandprev_onein the wrong order, corrupting the recurrence. - Forgetting the empty-list case, or indexing
nums[i - 2]without guarding short inputs in an array-based DP.
4: I-mplement
Implement the code to solve the algorithm.
def rob(nums):
prev_two = 0 # Best total considering houses up to i - 2
prev_one = 0 # Best total considering houses up to i - 1
for cash in nums:
# Either skip this house (keep prev_one)
# or rob it (prev_two + cash, since the neighbor is off-limits)
current = max(prev_one, prev_two + cash)
prev_two, prev_one = prev_one, current
return prev_one
5: R-eview
Review the code by running specific example(s) and recording values (watchlist) of your code’s variables along the way.
-
Input: nums = [1, 2, 3, 1]
- House 0 (cash 1): current = max(0, 0 + 1) = 1 → (prev_two, prev_one) = (0, 1)
- House 1 (cash 2): current = max(1, 0 + 2) = 2 → (1, 2)
- House 2 (cash 3): current = max(2, 1 + 3) = 4 → (2, 4)
- House 3 (cash 1): current = max(4, 2 + 1) = 4 → (4, 4)
- Output: 4
-
Input: nums = [2, 7, 9, 3, 1]
- House 0 (cash 2): current = max(0, 0 + 2) = 2 → (0, 2)
- House 1 (cash 7): current = max(2, 0 + 7) = 7 → (2, 7)
- House 2 (cash 9): current = max(7, 2 + 9) = 11 → (7, 11)
- House 3 (cash 3): current = max(11, 7 + 3) = 11 → (11, 11)
- House 4 (cash 1): current = max(11, 11 + 1) = 12 → (11, 12)
- Output: 12
-
Input: nums = []
- The loop never runs, so prev_one stays 0.
- Output: 0
6: E-valuate
Evaluate the performance of your algorithm and state any strong/weak or future potential work.
Assume N is the number of houses (the length of nums).
- Time Complexity:
O(N)because we make one constant-time rob/skip decision per house. - Space Complexity:
O(1)because only two rolling variables are kept, regardless of input size.