2560. House Robber IV
Problem Description
You have a row of consecutive houses along a street, where each house contains some money. A robber wants to steal from these houses, but has one strict rule: he cannot steal from two adjacent houses.
Given:
- An integer array 
numswherenums[i]represents the amount of money in thei-th house - An integer 
krepresenting the minimum number of houses the robber must steal from 
The capability of the robber is defined as the maximum amount of money he takes from any single house among all the houses he robs.
Your task is to find the minimum possible capability that allows the robber to steal from at least k houses while never robbing adjacent houses.
For example, if nums = [2, 3, 5, 9] and k = 2:
- If the robber steals from houses with values 2 and 5, his capability is 
max(2, 5) = 5 - If the robber steals from houses with values 2 and 9, his capability is 
max(2, 9) = 9 - If the robber steals from houses with values 3 and 9, his capability is 
max(3, 9) = 9 
The minimum capability would be 5 in this case.
The problem guarantees it's always possible to steal from at least k houses following the non-adjacent rule.
Intuition
The key insight is recognizing that this is an optimization problem where we need to find the minimum value of something (capability) that satisfies a constraint (robbing at least k houses).
Think about it this way: if we fix a capability value x, we can ask ourselves - "Can the robber steal from at least k houses without exceeding this capability?" This becomes a yes/no question. If the answer is yes, we might be able to do better with a smaller capability. If the answer is no, we need a larger capability.
This binary nature of the problem (yes/no for each capability value) suggests using binary search. The capability must be between the minimum and maximum values in the array. We can search this range to find the smallest capability that works.
For a given capability x, how do we check if it's possible to rob k houses? We want to maximize the number of houses we can rob while staying within capability x. A greedy approach works here: go through the houses from left to right, and whenever we see a house with value ≤ x that isn't adjacent to the last house we robbed, we should rob it. Why? Because robbing it now doesn't prevent us from robbing better houses later - we're just trying to maximize the count of houses robbed.
The beauty of this approach is that binary search naturally finds the minimum capability. As we search, we keep lowering our capability threshold whenever we find it's possible to rob k houses, eventually converging on the minimum possible value.
Learn more about Greedy, Binary Search and Dynamic Programming patterns.
Solution Approach
The solution combines binary search with a greedy validation function to find the minimum capability.
Binary Search Setup:
We search for the minimum capability in the range [0, max(nums)]. The capability cannot exceed the maximum value in the array since that's the most money in any single house.
Validation Function f(x):
For a given capability x, we check if the robber can steal from at least k houses:
- Initialize a counter 
cnt = 0for houses robbed andj = -2to track the index of the last robbed house (starting at-2ensures the first house can always be considered) - Iterate through each house 
iwith valuev:- Skip this house if 
v > x(exceeds our capability) or ifi == j + 1(adjacent to last robbed house) - Otherwise, rob this house: increment 
cntand updatej = i 
 - Skip this house if 
 - Return 
Trueifcnt >= k, meaning we can rob enough houses with this capability 
Binary Search Execution:
The code uses bisect_left(range(max(nums) + 1), True, key=f) which:
- Searches the range 
[0, max(nums)] - Looks for the leftmost (minimum) value where 
f(x)returnsTrue - This gives us the minimum capability that allows robbing at least 
khouses 
Why This Works:
- If capability 
xallows robbingkhouses, then any capability> xalso works (monotonic property) - Binary search efficiently finds the boundary between "not possible" and "possible"
 - The greedy approach in validation is optimal because we want to maximize the count of houses robbed for a given capability, and taking any valid house early doesn't prevent us from taking more houses later
 
Time Complexity: O(n * log(max(nums))) where n is the length of the array
Space Complexity: O(1) as we only use a few variables
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with nums = [2, 7, 9, 3, 1] and k = 2.
Step 1: Set up binary search range
- Minimum capability: 0
 - Maximum capability: max(nums) = 9
 - We'll search in range [0, 9] for the minimum capability that allows robbing at least 2 houses
 
Step 2: Binary search process
First iteration: mid = 4
- Check if capability 4 allows robbing 2 houses
 - Go through houses:
- House 0 (value=2): 2 ≤ 4, rob it! Count = 1, last robbed index = 0
 - House 1 (value=7): 7 > 4, skip (exceeds capability)
 - House 2 (value=9): 9 > 4, skip (exceeds capability)
 - House 3 (value=3): 3 ≤ 4 and not adjacent to house 0, rob it! Count = 2
 
 - Count = 2 ≥ k = 2 ✓
 - Since it works, try smaller: search [0, 4]
 
Second iteration: mid = 2
- Check if capability 2 allows robbing 2 houses
 - Go through houses:
- House 0 (value=2): 2 ≤ 2, rob it! Count = 1, last robbed index = 0
 - House 1 (value=7): 7 > 2, skip (exceeds capability)
 - House 2 (value=9): 9 > 2, skip (exceeds capability)
 - House 3 (value=3): 3 > 2, skip (exceeds capability)
 - House 4 (value=1): 1 ≤ 2 and not adjacent to house 0, rob it! Count = 2
 
 - Count = 2 ≥ k = 2 ✓
 - Since it works, try smaller: search [0, 2]
 
Third iteration: mid = 1
- Check if capability 1 allows robbing 2 houses
 - Go through houses:
- House 0 (value=2): 2 > 1, skip (exceeds capability)
 - House 1 (value=7): 7 > 1, skip (exceeds capability)
 - House 2 (value=9): 9 > 1, skip (exceeds capability)
 - House 3 (value=3): 3 > 1, skip (exceeds capability)
 - House 4 (value=1): 1 ≤ 1, rob it! Count = 1
 
 - Count = 1 < k = 2 ✗
 - Doesn't work, need larger: search [2, 2]
 
Final result: minimum capability = 2
The robber can steal from houses with values [2, 1] (indices 0 and 4) with a capability of 2, which is the minimum possible capability to rob at least 2 houses.
Solution Implementation
1class Solution:
2    def minCapability(self, nums: List[int], k: int) -> int:
3        def can_rob_k_houses(max_value):
4            """
5            Check if we can rob at least k houses with given maximum value constraint.
6            We cannot rob adjacent houses (must skip at least one house between robberies).
7          
8            Args:
9                max_value: Maximum value we're allowed to take from any house
10              
11            Returns:
12                True if we can rob at least k houses, False otherwise
13            """
14            robbed_count = 0
15            last_robbed_index = -2  # Initialize to -2 so first house at index 0 can be robbed
16          
17            for current_index, house_value in enumerate(nums):
18                # Skip if house value exceeds our limit or if it's adjacent to last robbed house
19                if house_value > max_value or current_index == last_robbed_index + 1:
20                    continue
21                  
22                # Rob this house
23                robbed_count += 1
24                last_robbed_index = current_index
25              
26            return robbed_count >= k
27      
28        # Binary search for minimum capability that allows robbing k houses
29        # Search range is from 0 to max(nums)
30        return bisect_left(range(max(nums) + 1), True, key=can_rob_k_houses)
311class Solution {
2    /**
3     * Finds the minimum capability needed to rob at least k houses
4     * @param nums Array representing the money in each house
5     * @param k Minimum number of houses that need to be robbed
6     * @return The minimum capability required
7     */
8    public int minCapability(int[] nums, int k) {
9        // Binary search range: minimum capability is 0, maximum is 10^9
10        int left = 0;
11        int right = (int) 1e9;
12      
13        // Binary search to find the minimum capability
14        while (left < right) {
15            int mid = (left + right) >> 1;  // Equivalent to (left + right) / 2
16          
17            // Check if with capability 'mid', we can rob at least k houses
18            if (countRobbableHouses(nums, mid) >= k) {
19                // If we can rob k or more houses, try a smaller capability
20                right = mid;
21            } else {
22                // If we can't rob k houses, we need higher capability
23                left = mid + 1;
24            }
25        }
26      
27        return left;
28    }
29  
30    /**
31     * Counts the maximum number of houses that can be robbed with given capability
32     * Adjacent houses cannot be robbed (security constraint)
33     * @param nums Array representing the money in each house
34     * @param maxCapability The maximum amount that can be stolen from a single house
35     * @return The maximum number of houses that can be robbed
36     */
37    private int countRobbableHouses(int[] nums, int maxCapability) {
38        int robbedCount = 0;
39        int lastRobbedIndex = -2;  // Initialize to -2 so first house at index 0 can be robbed
40      
41        for (int currentIndex = 0; currentIndex < nums.length; ++currentIndex) {
42            // Skip if house value exceeds capability or if it's adjacent to last robbed house
43            if (nums[currentIndex] > maxCapability || currentIndex == lastRobbedIndex + 1) {
44                continue;
45            }
46          
47            // Rob this house
48            ++robbedCount;
49            lastRobbedIndex = currentIndex;
50        }
51      
52        return robbedCount;
53    }
54}
551class Solution {
2public:
3    int minCapability(vector<int>& nums, int k) {
4        // Lambda function to check if we can rob at least k houses with max capability of maxCapability
5        auto canRobKHouses = [&](int maxCapability) {
6            int robbedCount = 0;  // Number of houses we can rob
7            int lastRobbedIndex = -2;  // Index of last robbed house (initialized to -2 to allow robbing house at index 0)
8          
9            for (int i = 0; i < nums.size(); ++i) {
10                // Skip if current house value exceeds our capability limit
11                // or if current house is adjacent to the last robbed house
12                if (nums[i] > maxCapability || i == lastRobbedIndex + 1) {
13                    continue;
14                }
15              
16                // Rob current house
17                ++robbedCount;
18                lastRobbedIndex = i;
19            }
20          
21            // Check if we can rob at least k houses
22            return robbedCount >= k;
23        };
24      
25        // Binary search on the answer (minimum capability needed)
26        int left = 0;
27        int right = *max_element(nums.begin(), nums.end());
28      
29        while (left < right) {
30            int mid = (left + right) >> 1;  // Equivalent to (left + right) / 2
31          
32            if (canRobKHouses(mid)) {
33                // If we can rob k houses with capability mid, try smaller capability
34                right = mid;
35            } else {
36                // If we cannot rob k houses with capability mid, need larger capability
37                left = mid + 1;
38            }
39        }
40      
41        return left;
42    }
43};
441/**
2 * Finds the minimum capability required to rob at least k houses
3 * where houses cannot be robbed consecutively
4 * @param nums - Array of values representing each house
5 * @param k - Minimum number of houses to rob
6 * @returns The minimum maximum value among selected houses
7 */
8function minCapability(nums: number[], k: number): number {
9    /**
10     * Checks if it's possible to rob at least k houses with maximum value <= maxValue
11     * @param maxValue - The maximum allowed value for any house to rob
12     * @returns True if we can rob at least k houses, false otherwise
13     */
14    const canRobWithCapability = (maxValue: number): boolean => {
15        let robbedCount = 0;
16        let lastRobbedIndex = -2; // Initialize to -2 to allow robbing the first house
17      
18        // Iterate through all houses
19        for (let currentIndex = 0; currentIndex < nums.length; currentIndex++) {
20            // Check if current house value is within capability and not adjacent to last robbed
21            if (nums[currentIndex] <= maxValue && currentIndex - lastRobbedIndex > 1) {
22                robbedCount++;
23                lastRobbedIndex = currentIndex;
24            }
25        }
26      
27        // Check if we can rob at least k houses
28        return robbedCount >= k;
29    };
30
31    // Binary search bounds: minimum is 1, maximum is the highest house value
32    let leftBound = 1;
33    let rightBound = Math.max(...nums);
34  
35    // Binary search to find the minimum capability
36    while (leftBound < rightBound) {
37        const midPoint = (leftBound + rightBound) >> 1; // Equivalent to Math.floor((left + right) / 2)
38      
39        if (canRobWithCapability(midPoint)) {
40            // If we can rob k houses with this capability, try a smaller one
41            rightBound = midPoint;
42        } else {
43            // If we cannot rob k houses with this capability, we need a larger one
44            leftBound = midPoint + 1;
45        }
46    }
47  
48    return leftBound;
49}
50Time and Space Complexity
Time Complexity: O(n × log m)
The algorithm uses binary search on the range [0, max(nums)] to find the minimum capability. The bisect_left function performs binary search, which takes O(log m) iterations where m = max(nums). For each iteration of the binary search, the helper function f(x) is called, which iterates through all n elements of the nums array once, taking O(n) time. Therefore, the overall time complexity is O(n × log m).
Space Complexity: O(1)
The algorithm only uses a constant amount of extra space. The helper function f(x) uses two variables cnt and j to track the count and the last selected index. The binary search itself doesn't require additional space proportional to the input size. Therefore, the space complexity is O(1).
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Greedy Strategy in Validation Function
The Pitfall: A common mistake is using a different greedy strategy that seems intuitive but doesn't maximize the number of houses robbed. For example:
- Trying to minimize gaps between robbed houses
 - Starting from houses with smaller values first
 - Using dynamic programming instead of greedy
 
Wrong Approach Example:
def can_rob_k_houses_wrong(max_value):
    # Wrong: Trying to be "smart" about which houses to skip
    robbed = []
    for i, v in enumerate(nums):
        if v <= max_value:
            if not robbed or i > robbed[-1] + 1:
                robbed.append(i)
            elif len(robbed) >= 2 and i > robbed[-2] + 2:
                # Wrong logic: trying to optimize placement
                robbed[-1] = i
    return len(robbed) >= k
The Solution: Stick to the simple greedy approach: take any valid house as soon as you can. This maximizes the count because:
- Taking a house early never prevents you from taking more houses later
 - Skipping a valid house might force you to skip more houses due to adjacency constraints
 
2. Off-by-One Error in Binary Search Range
The Pitfall:
Using range(max(nums)) instead of range(max(nums) + 1) in the binary search. This would miss the case where the minimum capability equals the maximum value in the array.
Wrong Code:
# This misses the case where answer = max(nums)
return bisect_left(range(max(nums)), True, key=can_rob_k_houses)
The Solution: Always include the upper bound in the search range:
return bisect_left(range(max(nums) + 1), True, key=can_rob_k_houses)
3. Incorrect Initial Value for Last Robbed Index
The Pitfall:
Initializing last_robbed_index to -1 instead of -2. This would incorrectly prevent robbing the house at index 0 when checking current_index == last_robbed_index + 1.
Wrong Code:
last_robbed_index = -1 # Wrong: 0 == (-1) + 1, so house 0 would be skipped
The Solution:
Initialize to -2 or any value less than -1:
last_robbed_index = -2 # Correct: ensures house at index 0 can be robbed
4. Using bisect_right Instead of bisect_left
The Pitfall:
Using bisect_right would find the rightmost position where True can be inserted, giving us a value one higher than the minimum capability.
Wrong Code:
# This returns minimum_capability + 1
return bisect_right(range(max(nums) + 1), True, key=can_rob_k_houses)
The Solution:
Use bisect_left to find the leftmost (minimum) value where the condition is satisfied:
return bisect_left(range(max(nums) + 1), True, key=can_rob_k_houses)
How many times is a tree node visited in a depth first search?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
Want a Structured Path to Master System Design Too? Don’t Miss This!