LC213 非相邻数最大和变种1|刷题打卡

170 阅读1分钟

本系列使用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个点,会有几种可能呢?

  1. 都没有抢
  2. 抢了头没抢尾
  3. 抢了位没抢头。

既然不能抢可以直接从数组中去掉,只看剩下的部分是不是就可以了?
题目要求返回最大的,就在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用户

四、总结:

  1. 遇到变种题先不要着急换数据结构,而是思考新的约束条件,给现状造成了什么样的改变。
  2. 递归和DP是一对好兄弟,通常情况下可以互相转换,像本题因为备忘录的原因就进行了转换。
  3. 本题需要注意DP数组的构建。

本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情