Skip to content

198. House Robber

class Solution:
    def rob(self, nums: list[int]) -> int:
        """Dynamic Programming (House Robber).

        Problem:
        Given a list of non-negative integers representing money in each house,
        determine the maximum amount you can rob without robbing two adjacent houses.

        Approach:
        Let dp[i] represent the maximum money that can be robbed up to house i.

        At each house, we have two choices:
            1. Rob the current house:
               nums[i] + dp[i-2]
            2. Skip the current house:
               dp[i-1]

        Transition:
            dp[i] = max(nums[i] + dp[i-2], dp[i-1])

        Base Cases:
            dp[0] = nums[0]
            dp[1] = max(nums[0], nums[1])

        Space Optimization:
        Instead of maintaining the full DP array, we only track:
            prev_2 → dp[i-2]
            prev_1 → dp[i-1]

        For each house:
            curr = max(nums[i] + prev_2, prev_1)

        Then shift:
            prev_2 = prev_1
            prev_1 = curr

        Final Answer:
            The maximum profit after processing all houses.

        Time Complexity:
            O(n)

        Space Complexity:
            O(1) (optimized version)
            O(n) (if using full DP array)
        """
        n = len(nums)
        if n == 1:
            return nums[0]

        prev_2 = nums[0]
        prev_1 = max(nums[:2])

        for i in range(2, n):
            curr = max(nums[i] + prev_2, prev_1)
            prev_2 = prev_1
            prev_1 = curr

        return max(prev_2, prev_1)