class Solution:
def rob(self, nums):
if not nums:
return 0
if len(nums) == 1:
return nums[0]
def rob_linear(houses):
prev, curr = 0, 0
for num in houses:
prev, curr = curr, max(curr, prev + num)
return curr
# Case 1: Do not rob the last house
case1 = rob_linear(nums[:-1])
# Case 2: Do not rob the first house
case2 = rob_linear(nums[1:])
return max(case1, case2)
class Solution {
public:
int rob(vector<int>& nums) {
if (nums.empty()) return 0;
if (nums.size() == 1) return nums[0];
return max(robLinear(nums, 0, nums.size() - 2),
robLinear(nums, 1, nums.size() - 1));
}
private:
int robLinear(const vector<int>& nums, int start, int end) {
int prev = 0, curr = 0;
for (int i = start; i <= end; ++i) {
int temp = curr;
curr = max(curr, prev + nums[i]);
prev = temp;
}
return curr;
}
};
class Solution {
public int rob(int[] nums) {
if (nums.length == 0) return 0;
if (nums.length == 1) return nums[0];
return Math.max(robLinear(nums, 0, nums.length - 2),
robLinear(nums, 1, nums.length - 1));
}
private int robLinear(int[] nums, int start, int end) {
int prev = 0, curr = 0;
for (int i = start; i <= end; i++) {
int temp = curr;
curr = Math.max(curr, prev + nums[i]);
prev = temp;
}
return curr;
}
}
var rob = function(nums) {
if (!nums.length) return 0;
if (nums.length === 1) return nums[0];
function robLinear(houses) {
let prev = 0, curr = 0;
for (let num of houses) {
let temp = curr;
curr = Math.max(curr, prev + num);
prev = temp;
}
return curr;
}
// Exclude last house
let case1 = robLinear(nums.slice(0, nums.length - 1));
// Exclude first house
let case2 = robLinear(nums.slice(1));
return Math.max(case1, case2);
};
You are given an array nums
where each element represents the amount of money stashed in a house arranged in a circle. The houses are adjacent, and if you rob two directly connected houses, the police will be alerted. Your task is to determine the maximum amount of money you can rob tonight without robbing any two adjacent houses. Because the houses are arranged in a circle, the first and last houses are also considered adjacent.
At first glance, this problem looks similar to the classic "House Robber" problem, where houses are in a straight line. In that version, you can use dynamic programming to decide for each house whether to rob it or skip it, based on the best outcome so far.
However, the circular arrangement adds a twist: the first and last houses are now adjacent. This means you cannot simply apply the same logic to the entire array, because robbing the first house prevents you from robbing the last house, and vice versa.
The brute-force approach would try every possible combination, but that's not efficient. We need a smarter way—one that breaks the problem into manageable pieces. The key insight is to split the problem into two scenarios:
For each scenario, the problem reduces to the classic "House Robber" problem on a line, which we can solve efficiently.
Let's break down the steps to solve the problem:
prev
(total up to house i-2) and curr
(total up to house i-1).This approach ensures that we never rob both the first and last house, and leverages efficient dynamic programming for optimal performance.
Let's walk through an example with nums = [2, 3, 2]
:
[2, 3]
.[3, 2]
.This process ensures we never rob both the first and last house, and always get the best possible total.
Brute-force approach: Would try all valid combinations, leading to exponential time complexity: O(2n).
Optimized DP approach:
The House Robber II problem builds on the classic dynamic programming approach, but adds the twist of a circular house arrangement. By smartly splitting the problem into two linear subproblems—each excluding either the first or last house—we avoid the adjacency issue and apply efficient DP. This leads to a solution that's both elegant and optimal, with linear time and constant space complexity.