LC198 数组内非相邻元素最大合问题|刷题打卡

172 阅读2分钟

本系列使用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, the only constraint stopping you from r
//obbing each of them is that adjacent houses have security system connected and i
//t 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 eac
//h house, determine the maximum amount of money you can rob tonight without alert
//ing the police. 
//
// 
// Example 1: 
//
// 
//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 2: 
//
// 
//Input: nums = [2,7,9,3,1]
//Output: 12
//Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 
//(money = 1).
//             Total amount you can rob = 2 + 9 + 1 = 12.
// 
//
// 
// Constraints: 
//
// 
// 0 <= nums.length <= 100 
// 0 <= nums[i] <= 400 

二、思路分析:
首先读题,看起来花里胡哨的,总结起来就是文章标题里的数组内非相邻元素最大合问题。这是道M,所以依然还有坑等着你。
题目会迷惑你让你以为只能隔一个抢一个,实际上你隔几个抢都行,只要不相邻。
注意一个思维误区: [1,2,3,4,5],抢了1后如果直接求下一次的最好选择,你会直接抢到5,然后发现才总共6,所以局部最优解绝不是下次抢所有剩余房子中的哪个的结果最大,切记切记。而应该是在抢当前的还是下一个中选择,尽量多抢是核心要求,而这道题的局部最优解是在抢当前和抢下一个中产生的,并不是抢当前和抢剩余所有中最大的那个

所以能通过题目花里胡哨/复杂的描述总结出核心问题,并且找到可能的坑,这个才是这期的重点内容

然后直接可以写出一版递归代码:

class Solution {
    public int rob(int[] nums) {
        return rob(nums,0);
    }
    int rob(int[] nums,int index){
        if(index >= nums.length){
            return 0;
        }
        // 不抢当前直接去下一家和抢当前的然后去下下一家比较
        return Math.max(rob(nums,index + 1),nums[index] + rob(nums,index+2));
    }
}

一运行,超时了,思考如何优化:
涉及到递归就有一个剪枝问题,从上面代码可知,可以缓存每次index的结果。
优化后时间超过100%。

三、AC 代码:

class Solution {
    int[] indexValue;

    public int rob(int[] nums) {
        indexValue = new int[nums.length];
        Arrays.fill(indexValue, -1);
        return rob(nums, 0);
    }

    // 代码超时,思考为什么:只要用了递归,必有的性能优化就是剪枝。缓存index的值
    int rob(int[] nums, int index) {
        if (index >= nums.length) {
            return 0;
        }
        if (indexValue[index] != -1) {
            return indexValue[index];
        }
        // 不抢当前直接去下一家和抢当前的然后去下下一家比较
        int result = Math.max(rob(nums, index + 1), nums[index] + rob(nums, index + 2));
        indexValue[index] = result;
        return result;
    }
}
解答成功:
        执行耗时:0 ms,击败了100.00% 的Java用户
        内存消耗:36.1 MB,击败了7.86% 的Java用户

四、总结:

  1. 练习透过花里胡哨或者复杂的问题描述直击核心问题的能力。
  2. 只要写了递归,就一定要思考如何剪枝,剪枝和递归必然一起出现。
  3. 弄清什么是真正的局部最优解,比如这道题直接找接下来最大的值就错了,局部最优解是有范围的,找到范围最重要。

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