The Problem

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed. All houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, adjacent houses have a security system connected, and it will automatically contact the police if two adjacent houses were broken into on the same night.

Given an integer array nums representing the amount of money of each house, return the maximum amount of money you can rob tonight without alerting the police.

Check LeetCode link for more details and example input & output.

A Dynamic Programming Solution

It’s not allowed to rob both house 0 and house nums.length - 1, because they are adjacent hourses. So the problem becomes finding the greater amount between robbing houses except the first and robbing house except the last.

The two sub problems, robbing houses except the first and robbing house except the last, can be solved using a bottom up approach described in this post.

A Java solution is listed below.

  public int rob(int[] nums) {
      if (nums.length == 1) { // an edge case
          return nums[0];
      }
      return Math.max(rob1(nums, 0, nums.length - 2), rob1(nums, 1, nums.length - 1));
  }

  /*
  This method is to solve the 'house robber I' problem.
  Houses are located in `nums`, from `start` to `end`.
   */
  int rob1(int[] nums, int start, int end) {
      int r1 = nums[end];
      int r2 = 0;
      for (int i = end - 1; i >= start; i--) {
          int tmp = r1;
          r1 = Math.max(nums[i] + r2, r1);
          r2 = tmp;
      }
      return r1;
  }

The time complexity is O(n).

A Recursive Approach

Below is a straightforward recursive solution.

  public int rob(int[] nums) {
      int result = 0;
      for (int i = 0; i < nums.length; i++) {
          result = Math.max(result, maxRobStartAtIndex(nums, i, i == 0));
      }
      return result;
  }

  // max rob, starting at the house `index`
  int maxRobStartAtIndex(int[] nums, int index, boolean firstPicked) {
      int result = nums[index];
      for (int i = index + 2; i < nums.length; i++) {
          if (firstPicked && i == nums.length - 1) {
              continue;
          }
          result = Math.max(result, nums[index] + maxRobStartAtIndex(nums, i, firstPicked));
      }
      return result;
  }