力扣198——打家劫舍

这次准备连讲三道题,这道题就是最基础的,利用动态规划可以解决。

原题

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

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

示例 :

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

原题url:https://leetcode-cn.com/problems/house-robber/

解题

动态规划-自顶向下

动态规划大家应该很容易就想到,如果偷了当前的金钱,下一家就不能偷,如果不偷当前的金钱,可以考虑下一家。比如:

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

  • 从第4家开始能偷到的最多金钱
  • 第3家的金钱加上从第5家开始能偷到的最多金钱
    上面两者谁更多,就选择怎么做。

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

接下来看看代码:

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/

公众号:健程之道

原文地址:https://www.cnblogs.com/death00/p/12219458.html

时间: 2024-10-29 18:09:56

力扣198——打家劫舍的相关文章

力扣337——打家劫舍 III

这一篇也是基于"打家劫舍"的扩展,需要针对特殊情况特殊考虑,当然其本质还是动态规划,优化时需要考虑数据结构. 原题 在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区.这个地区只有一个入口,我们称之为"根". 除了"根"之外,每栋房子有且只有一个"父"房子与之相连.一番侦察之后,聪明的小偷意识到"这个地方的所有房屋的排列类似于一棵二叉树". 如果两个直接相连的房子在同一天晚上被打劫,房屋

卡特兰数(Catalan number)-力扣96

卡特兰数又称卡塔兰数,英文名Catalan number,是组合数学中一个常出现在各种计数问题中出现的数列.以比利时的数学家欧仁·查理·卡塔兰 (1814–1894)的名字来命名,其前几项为 : 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 244662

力扣算法题—042接雨水

1 #include"000库函数.h" 2 //一点头绪都没有 3 //然后就自己按自己的意思来一遍 4 //好像没有用算法 5 //16ms,让我激动一把 6 7 class Solution { 8 public: 9 int trap(vector<int>& height) { 10 if (height.size() < 2)return 0; 11 int s = 0;//起始点 12 int e = 0;//终止点 13 int v = 0;/

力扣题目汇总(加一,旋转数组,整数反转)

力扣题目汇总(加一,旋转数组,整数反转) 加一 1.题目描述 给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一. 最高位数字存放在数组的首位, 数组中每个元素只存储一个数字. 你可以假设除了整数 0 之外,这个整数不会以零开头. 示例 1: 输入: [1,2,3] 输出: [1,2,4] 解释: 输入数组表示数字 123. 示例 2: 输入: [4,3,2,1] 输出: [4,3,2,2] 解释: 输入数组表示数字 4321. 2.解题思路 #错误思路 列表最后一位加1,判断最后

力扣题目汇总(转换成小写字母,唯一摩尔斯密码,有序数组平方)

力扣题目汇总(转换成小写字母,唯一摩尔斯密码,有序数组平方) 转换成小写字母 1.题目描述 实现函数 ToLowerCase(),该函数接收一个字符串参数 str,并将该字符串中的大写字母转换成小写字母,之后返回新的字符串. 示例 1: 输入: "Hello" 输出: "hello" 示例 2: 输入: "here" 输出: "here" 示例 3: 输入: "LOVELY" 输出: "lovel

力扣sql题

组合两个表 表1: Person +-------------+---------+| 列名 | 类型 |+-------------+---------+| PersonId | int || FirstName | varchar || LastName | varchar |+-------------+---------+PersonId 是上表主键表2: Address +-------------+---------+| 列名 | 类型 |+-------------+-------

[力扣]144_二叉树的前序遍历

/* 给定一个二叉树,返回它的 前序 遍历. 示例: 输入: [1,null,2,3] 1 2 / 3 输出: [1,2,3] 进阶: 递归算法很简单,你可以通过迭代算法完成吗? 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/binary-tree-preorder-traversal 著作权归领扣网络所有.商业转载请联系官方授权,非商业转载请注明出处. */ 方法一:常规递归方式,用C语言实现(根左右) 代码实现: /** * Note

力扣——分糖果 II

排排坐,分糖果. 我们买了一些糖果 candies,打算把它们分给排好队的 n = num_people 个小朋友. 给第一个小朋友 1 颗糖果,第二个小朋友 2 颗,依此类推,直到给最后一个小朋友 n 颗糖果. 然后,我们再回到队伍的起点,给第一个小朋友 n + 1 颗糖果,第二个小朋友 n + 2 颗,依此类推,直到给最后一个小朋友 2 * n 颗糖果. 重复上述过程(每次都比上一次多给出一颗糖果,当到达队伍终点后再次从队伍起点开始),直到我们分完所有的糖果.注意,就算我们手中的剩下糖果数不

力扣——二叉树的层平均值

给定一个非空二叉树, 返回一个由每层节点平均值组成的数组. 示例 1: 输入:    3   / \  9  20    /  \   15   7输出: [3, 14.5, 11]解释:第0层的平均值是 3,  第1层是 14.5, 第2层是 11. 因此返回 [3, 14.5, 11]. 注意: 节点值的范围在32位有符号整数范围内. 来源:力扣(LeetCode)链接:https://leetcode-cn.com/problems/average-of-levels-in-binary-