题目描述(中等难度)
198 题 House Robber 的延续。一个数组,每个元素代表商店的存款,一个小偷晚上去偷商店,问最多能偷多少钱。有一个前提,不能偷相邻的商店,不然警报会响起。这道题的不同之处在于,商店是环形分布,所以第一家和最后一家也算作相邻商店。
解法一
这道题和 198 题 的区别在题目描述中也指出来了,即偷了第一家就不能偷最后一家。
所以顺理成章,偷不偷第一家我们单独考虑一下即可。
偷第一家,也就是求出在前 n - 1
家中偷的最大收益,也就是不考虑最后一家的最大收益。
不偷第一家,也就是求第 2
家到最后一家中偷的最大收益,也就是不考虑第一家的最大收益。
然后只需要返回上边两个最大收益中的较大的即可。
图示的话就是下边的两种范围。
X X X X X X ^ ^ X X X X X X ^ ^
我们看一下之前求全部商店的最大收益的代码,把最后优化的代码直接贴过来了,大家可以到 198 题 看详细的。
public int rob(int[] nums) { int n = nums.length; if (n == 0) { return 0; } if (n == 1) { return nums[0]; } int pre = 0; int cur = nums[0]; for (int i = 2; i <= n; i++) { int temp = cur; cur = Math.max(pre + nums[i - 1], cur); pre = temp; } return cur; }
为了适应这道题的算法,我们可以对上边的代码进行改造。增加所偷的商店的范围的参数。
public int robHelper(int[] nums, int start, int end) { int n = nums.length; if (n == 0) { return 0; } if (n == 1) { return nums[0]; } int pre = 0; int cur = nums[start]; for (int i = start + 2; i <= end; i++) { int temp = cur; cur = Math.max(pre + nums[i - 1], cur); pre = temp; } return cur; }
有了上边的代码,这道题就非常好写了。
public int rob(int[] nums) { //考虑第一家 int max1 = robHelper(nums, 0, nums.length - 1); //不考虑第一家 int max2 = robHelper(nums, 1, nums.length); return Math.max(max1, max2); } public int robHelper(int[] nums, int start, int end) { int n = nums.length; if (n == 0) { return 0; } if (n == 1) { return nums[0]; } int pre = 0; int cur = nums[start]; for (int i = start + 2; i <= end; i++) { int temp = cur; cur = Math.max(pre + nums[i - 1], cur); pre = temp; } return cur; }
总
这道题通过分类的思想,成功将新问题化解到了已求解问题上,这个思想也经常遇到。