Try all valid subsets (no two adjacent, including the wrap-around). For each subset, compute the sum and track the maximum. This is exponential in the number of houses.
Why it works
Exhaustively checking all combinations guarantees the optimal is found. The circular constraint is handled by explicitly checking that the first and last house aren't both selected.
Why we can do better
Key insight: The circular constraint means either house 0 is NOT robbed, or house n-1 is NOT robbed (or both). This lets us split the problem into two linear House Robber runs: one on nums[0..n-2] (exclude last), one on nums[1..n-1] (exclude first). The answer is the max of both.
Java โ Brute force (two linear passes, simpler)
classSolution {
publicintrob(int[] nums) {
if (nums.length == 1) return nums[0];
return Math.max(robLine(nums, 0, nums.length-2),
robLine(nums, 1, nums.length-1));
}
privateintrobLine(int[] nums, int lo, int hi) {
int prev2 = 0, prev1 = 0;
for (int i = lo; i <= hi; i++) {
int cur = Math.max(prev1, prev2 + nums[i]);
prev2 = prev1; prev1 = cur;
}
return prev1;
}
}
Break the circle by observing: in any optimal solution, either the first house is excluded or the last house is excluded (or both). Run the standard House Robber algorithm twice โ once on nums[0..n-2], once on nums[1..n-1] โ and return the maximum.
๐ก Mental model: Imagine guests sitting at a round dinner table. You want to pass a dessert plate but can't serve two adjacent guests. The problem: the first and last guests sit next to each other. Solution: imagine cutting the circular table into a line at one seat. You cut it two ways โ once removing the host's seat from the left end, once from the right. Solve the straight-line seating problem each way and pick whichever serves more guests. The cut guarantees you never serve both endpoints of the original circle.
Algorithm
Base case: if n == 1, return nums[0] (only one house, rob it).
Pass 1: run House Robber on nums[0..n-2] (might include first, definitely excludes last).
Pass 2: run House Robber on nums[1..n-1] (might include last, definitely excludes first).
Return max(pass1, pass2).
๐ฏ When to recognize this pattern:
Any "circular array with adjacent constraint" reduces to two linear subproblems.
The signal is: "array is circular AND you can't pick adjacent elements." This reduction also applies to LC 188/309 variants with circular constraints.
Why this covers all cases:
If the optimal solution includes house 0, it can't include house n-1 โ so it's captured by pass 1.
If it includes house n-1, it can't include house 0 โ captured by pass 2.
If it includes neither, it's captured by both passes. Therefore max(pass1, pass2) is always correct.
Prerequisite: This problem requires having solved LC 198 (House Robber) first. The entire solution is just "run LC 198 twice on different subarrays." Understanding the linear version's DP recurrence is essential before attempting the circular version.
07
Section Seven ยท Edge Cases
Edge Cases & Pitfalls
Case
Input
Why It Matters
Single house
[5]
No circular issue. Must handle as special case โ robLine(0,-1) would return 0.
Two houses
[1,2]
Adjacent in circle. Max of each alone: max(1,2)=2.
Three houses
[2,3,2]
Can only rob one. max(pass1=[2,3]โ3, pass2=[3,2]โ3)=3.
All equal
[5,5,5,5]
Both passes give 10. Answer=10.
Huge last house
[1,1,1,100]
Pass 2 captures the 100. pass1=[1,1,1]โ2, pass2=[1,1,100]โ101. Answer=101.
โ Common Mistake: Forgetting the n == 1 special case. With only one house, nums[0..n-2] = nums[0..-1] is empty, and nums[1..n-1] = nums[1..0] is also empty โ both passes return 0. But the correct answer is nums[0]. Always guard if (n == 1) return nums[0] before the two-pass logic.