本系列使用IDEA+LEETCODE EDITOR插件,题目描述统一英文。
题目链接
一、题目描述:
//You are a professional robber planning to rob houses along a street. Each hous
//e has a certain amount of money stashed. All houses at this place are arranged i
//n a circle. That means the first house is the neighbor of the last one. Meanwhil
//e, adjacent houses have a security system connected, and it will automatically c
//ontact 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, r
//eturn the maximum amount of money you can rob tonight without alerting the polic
//e.
//
//
// Example 1:
//
//
//Input: nums = [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: nums = [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.
//
//
// Example 3:
//
//
//Input: nums = [0]
//Output: 0
//
//
//
// Constraints:
//
//
// 1 <= nums.length <= 100
// 0 <= nums[i] <= 1000
二、思路分析:
原型可参见之前的这篇文章。
从之前的数组变成了一个首尾相连的环,
这时之前的解法已经不适用了,遇到首尾都抢的情况就不行了。这时候不要贸然去改变之前的数据结构比如换了个LinkedList去想这道题。
而是在之前的基础上思考,新的约束带来了哪些影响?
这时依然考虑这是一个数组,而不去想它是个圆,对于首尾2个点,会有几种可能呢?
- 都没有抢
- 抢了头没抢尾
- 抢了位没抢头。
既然不能抢可以直接从数组中去掉,只看剩下的部分是不是就可以了?
题目要求返回最大的,就在3个结果集中找最大的。
无非就是从之前的算一次变成了算3次。
这时候聪明的你应该注意到了,如果只取中间的,出现了中间的两头有一个没抢,那不就浪费了原来的两头中的一个么?
所以中间的一定小于两头只有一个不选的可能。这样我们的计算又少了一次,NICE。
落实到代码里,去掉就是移动左右index的操作。
然而在改代码的时候会发现,2种给定的数据,之前的备忘录没法用了,如果硬要用就要定义2个备忘录。这样的代码太不优雅。
所以我们对之前的递归代码,修改成DP模式。
不要贸然去改变之前的数据结构而是在之前的基础上思考,新的约束带来了哪些影响,这才是本期的重点。
三、AC 代码:
class Solution {
public int rob(int[] nums) {
if(nums.length == 0){
return 0;
}
if(nums.length == 1){
return nums[0];
}
return Math.max(rob(nums, 0,nums.length-2),rob(nums, 1,nums.length-1));
}
int rob(int[] nums, int indexLeft, int indexRight) {
// 要考虑到是抢下一家的下一家,indexRight+1 是总长度 indexRight+1 +2 是预留的长度
int[] dp = new int[indexRight + 3];
// 因为不知道抢不抢第一家,从最后一家反推,所以没有初始化过程
// 递归是从头到尾,这道题的dp是从尾到头
for (int i = indexRight; i >= indexLeft; i--) {
dp[i] = Math.max(dp[i + 1],nums[i] + dp[i + 2]);
}
// 很关键,要返回indexLeft位置的值,而不是0
return dp[indexLeft];
}
}
解答成功:
执行耗时:0 ms,击败了100.00% 的Java用户
内存消耗:35.8 MB,击败了65.73% 的Java用户
四、总结:
- 遇到变种题先不要着急换数据结构,而是思考新的约束条件,给现状造成了什么样的改变。
- 递归和DP是一对好兄弟,通常情况下可以互相转换,像本题因为备忘录的原因就进行了转换。
- 本题需要注意DP数组的构建。
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情