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 security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.
Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.
Example 1:
Input: [2,3,2] Output: 3 Explanation: You cannot rob house 1 (money = 2) and then rob house 3 (money = 2), because they are adjacent houses.
Example 2:
Input: [1,2,3,1] Output: 4 Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3). Total amount you can rob = 1 + 3 = 4.
Credits:
Special thanks to @Freezen for adding this problem and creating all test cases.
這道題是之前那道 House Robber 的拓展,現在房子排成了一個圓圈,則如果搶了第一家,就不能搶最后一家,因為首尾相連了,所以第一家和最后一家只能搶其中的一家,或者都不搶,那這里變通一下,如果把第一家和最后一家分別去掉,各算一遍能搶的最大值,然后比較兩個值取其中較大的一個即為所求。那只需參考之前的 House Robber 中的解題方法,然后調用兩邊取較大值,代碼如下:
解法一:
class Solution { public: int rob(vector<int>& nums) { if (nums.size() <= 1) return nums.empty() ? 0 : nums[0]; return max(rob(nums, 0, nums.size() - 1), rob(nums, 1, nums.size())); } int rob(vector<int> &nums, int left, int right) { if (right - left <= 1) return nums[left]; vector<int> dp(right, 0); dp[left] = nums[left]; dp[left + 1] = max(nums[left], nums[left + 1]); for (int i = left + 2; i < right; ++i) { dp[i] = max(nums[i] + dp[i - 2], dp[i - 1]); } return dp.back(); } };
當然,我們也可以使用兩個變量來代替整個 DP 數組,講解與之前的帖子 House Robber 相同,分別維護兩個變量 robEven 和 robOdd,顧名思義,robEven 就是要搶偶數位置的房子,robOdd 就是要搶奇數位置的房子。所以在遍歷房子數組時,如果是偶數位置,那么 robEven 就要加上當前數字,然后和 robOdd 比較,取較大的來更新 robEven。這里就看出來了,robEven 組成的值並不是只由偶數位置的數字,只是當前要搶偶數位置而已。同理,當奇數位置時,robOdd 加上當前數字和 robEven 比較,取較大值來更新 robOdd,這種按奇偶分別來更新的方法,可以保證組成最大和的數字不相鄰,最后別忘了在 robEven 和 robOdd 種取較大值返回,代碼如下:
解法二:
class Solution { public: int rob(vector<int>& nums) { if (nums.size() <= 1) return nums.empty() ? 0 : nums[0]; return max(rob(nums, 0, nums.size() - 1), rob(nums, 1, nums.size())); } int rob(vector<int> &nums, int left, int right) { int robEven = 0, robOdd = 0; for (int i = left; i < right; ++i) { if (i % 2 == 0) { robEven = max(robEven + nums[i], robOdd); } else { robOdd = max(robEven, robOdd + nums[i]); } } return max(robEven, robOdd); } };
另一種更為簡潔的寫法,講解與之前的帖子 House Robber 相同,我們使用兩個變量 rob 和 notRob,其中 rob 表示搶當前的房子,notRob 表示不搶當前的房子,那么在遍歷的過程中,先用兩個變量 preRob 和 preNotRob 來分別記錄更新之前的值,由於 rob 是要搶當前的房子,那么前一個房子一定不能搶,所以使用 preNotRob 加上當前的數字賦給 rob,然后 notRob 表示不能搶當前的房子,那么之前的房子就可以搶也可以不搶,所以將 preRob 和 preNotRob 中的較大值賦給 notRob,參見代碼如下:
解法三:
class Solution { public: int rob(vector<int>& nums) { if (nums.size() <= 1) return nums.empty() ? 0 : nums[0]; return max(rob(nums, 0, nums.size() - 1), rob(nums, 1, nums.size())); } int rob(vector<int> &nums, int left, int right) { int rob = 0, notRob = 0; for (int i = left; i < right; ++i) { int preRob = rob, preNotRob = notRob; rob = preNotRob + nums[i]; notRob = max(preRob, preNotRob); } return max(rob, notRob); } };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/213
類似題目:
Non-negative Integers without Consecutive Ones
參考資料:
https://leetcode.com/problems/house-robber-ii/
https://leetcode.com/problems/house-robber-ii/discuss/59929/Java-clean-short-solution-DP