House Robber II

Since this question is a follow-up to House Robber, we can assume we already have a way to solve the simpler question, i.e. given a 1 row of house, we know how to rob them. So we already have such a helper function. We modify it a bit to rob a given range of houses.

private int rob(int[] num, int lo, int hi) {
    int include = 0, exclude = 0;
    for (int j = lo; j <= hi; j++) {
        int i = include, e = exclude;
        include = e + num[j];
        exclude = Math.max(e, i);
    }
    return Math.max(include, exclude);
}

Now the question is how to rob a circular row of houses. It is a bit complicated to solve like the simpler question. It is because in the simpler question whether to rob num[lo] is entirely our choice. But, it is now constrained by whether num[hi] is robbed.

However, since we already have a nice solution to the simpler problem. We do not want to throw it away. Then, it becomes how can we reduce this problem to the simpler one. Actually, extending from the logic that if house i is not robbed, then you are free to choose whether to rob house i + 1, you can break the circle by assuming a house is not robbed.

For example, 1 -> 2 -> 3 -> 1 becomes 2 -> 3 if 1 is not robbed.

Since every house is either robbed or not robbed and at least half of the houses are not robbed, the solution is simply the larger of two cases with consecutive houses, i.e. house i not robbed, break the circle, solve it, or house i + 1 not robbed. Hence, the following solution. I chose i = n and i + 1 = 0 for simpler coding. But, you can choose whichever two consecutive ones.

public int rob(int[] nums) {
    if (nums.length == 1) return nums[0];
    return Math.max(rob(nums, 0, nums.length - 2), rob(nums, 1, nums.length - 1));
}
class Solution(object):
    def rob(self, nums):
        if not nums:  # 就算是 follow up question,还是一定要记住检查 corner case
            return 0
        if len(nums) <= 2: # 就算是 warp 一个简单 case,在复杂 case 的外部还是要检查好 corner case
            return max(nums)
        return max(self.robNoCircle(nums[0: -1]), self.robNoCircle(nums[1:]))

    def robNoCircle(self, nums):
        if not nums:
            return 0
        if len(nums) <= 2:
            return max(nums)
        dp = [0] * 3
        dp[0] = nums[0]
        # dp[1] = nums[1] # 同一个问题出现了两遍,定义好 dp 规则后,初始化非常重要,写完之后严格按照要求再查一遍
        dp[1] = max(nums[0], nums[1])
        for i in range(2, len(nums)):
            dp[i % 3] = max(dp[(i + 3 - 1) % 3], nums[i] + dp[(i + 3 - 2) % 3])
        return dp[(len(nums) - 1) % 3]

Last updated