力扣198——打家劫舍

此次准备连讲三道题,这道题就是最基础的,利用动态规划能够解决。
<!-- more -->git

原题

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有必定的现金,影响你偷窃的惟一制约因素就是相邻的房屋装有相互连通的防盗系统,若是两间相邻的房屋在同一夜被小偷闯入,系统会自动报警。github

给定一个表明每一个房屋存放金额的非负整数数组,计算你在不触动警报装置的状况下,可以偷窃到的最高金额。segmentfault

示例 :数组

输入: [1,2,3,1]
输出: 4
解释: 偷窃 1 号房屋 (金额 = 1) ,而后偷窃 3 号房屋 (金额 = 3)。
     偷窃到的最高金额 = 1 + 3 = 4 。

原题url:https://leetcode-cn.com/probl...优化

解题

动态规划-自顶向下

动态规划你们应该很容易就想到,若是偷了当前的金钱,下一家就不能偷,若是不偷当前的金钱,能够考虑下一家。好比:url

小偷到了第3家,他有两个选择:不偷第3家以后去第4家、偷完第3家以后去第5家。这时他须要比较的是:spa

  • 从第4家开始能偷到的最多金钱
  • 第3家的金钱加上从第5家开始能偷到的最多金钱

上面二者谁更多,就选择怎么作。code

那主要目的就是要求出从当前到结尾能够偷到的最大值,为了避免重复计算,能够利用一个数组记录中间结果。递归

接下来看看代码:leetcode

class Solution {
    public int rob(int[] nums) {
        if (nums.length == 0) {
            return 0;
        }

        // 存储中间结果
        int[] result = new int[nums.length];
        Arrays.fill(result, -1);
        // 动态规划,利用中间结果,寻找最大值
        dp(0, nums, result);
        return result[0];
    }

    public int dp(int start, int[] nums, int[] result) {
        if (start >= nums.length) {
            return 0;
        }

        if (result[start] != -1) {
            return result[start];
        }

        result[start] = Math.max(
            // 选择偷当前的家
            nums[start] + dp(start + 2, nums, result),
            // 选择不偷当前的家
            dp(start + 1, nums, result)
        );

        return result[start];
    }
}

提交OK。

动态规划-自底向上

上面的写法实际上是从头向尾考虑,写法上是递归。那么若是想不用递归呢?毕竟递归也是有缺陷的,若是次数过多,总调用栈就会很长。那咱们来改造一下。

若是咱们是从尾向头考虑呢?也就是从最后一家开始,选择偷或者不偷,最终到第一家。思想上仍是很好理解的,和上面差很少,让咱们看看代码:

class Solution {
    public int rob(int[] nums) {
        if (nums.length == 0) {
            return 0;
        }

        // 存储中间结果
        int[] result = new int[nums.length + 2];
        // 动态规划,利用中间结果,寻找最大值
        for (int i = nums.length - 1; i >= 0; i--) {
            result[i] = Math.max(
                // 当前不偷
                result[i + 1],
                // 当前偷
                nums[i] + result[i + 2]
            );
        }
        return result[0];
    }
}

提交也是OK的。

空间上的优化

咱们仔细观察一下上面的写法,其实每次你利用到的中间状态只有两个,下一个位置和再下一个位置。那么此时咱们也能够用三个变量来存储就够了,两个存储以后的值,还有一个存储当前的值。

让咱们来看看代码:

class Solution {
    public int rob(int[] nums) {
        if (nums.length == 0) {
            return 0;
        }

        // 存储当前位置,下一个位置,和再下一个位置的结果
        int current = 0;
        int next_1 = 0;
        int next_2 = 0;
        // 动态规划,利用中间结果,寻找最大值
        for (int i = nums.length - 1; i >= 0; i--) {
            current = Math.max(
                // 当前不偷
                next_1,
                // 当前偷
                nums[i] + next_2
            );
            next_2 = next_1;
            next_1 = current;
        }
        return current;
    }
}

总结

以上就是这道题目个人解答过程了,不知道你们是否理解了。这道题主要利用动态规划就能够解决,能够改写递归,优化空间复杂度。

有兴趣的话能够访问个人博客或者关注个人公众号、头条号,说不定会有意外的惊喜。

https://death00.github.io/

公众号:健程之道

相关文章
相关标签/搜索