ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【每日力扣37】打家劫舍

2022-01-30 23:31:57  阅读:164  来源: 互联网

标签:偷窃 nums int 37 力扣 房屋 数组 打家劫舍 dp


一、题目[LeetCode-198]

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

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

示例 1:

输入:[1,2,3,1]

输出:4

解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。

     偷窃到的最高金额 = 1 + 3 = 4 。

示例 2:

输入:[2,7,9,3,1]

输出:12

解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。

     偷窃到的最高金额 = 2 + 9 + 1 = 12 。

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 400

二、思路

动态规划

由题意得,即求数组nums的最大元素和(其中取出的元素不能相邻),恰好与《day36 最大子序和》相反,该题要求数组中最大的连续的元素和。设dp[i][0]表示经过第i家(i从0计)后选择不偷该家的钱,即遍历到数组第i个元素后选择不加上该元素;dp[i][1]则表示遍历到数组第i个元素后选择加上该元素。

对于dp[i][0],要么是第i-1家没偷(即dp[i-1][0]),要么是第i-1家偷了(即dp[i-1][1]),直接取二者最大者即可;对于dp[i][1],只能是第i-1家没偷,然后才能偷第i家。

因此得出动态规划转移方程:

dp[i][0] = max(dp[i-1][0], dp[i-1][1])

dp[i][1] = dp[i-1][0] + nums[i]

初始条件:dp[0][0] = 0, dp[0][1] = nums[0].

class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        vector<vector<int>> dp(n);
        for(int i = 0; i < n; i++)
            dp[i] = vector<int>(2);//声明动态规划数组
        dp[0][0] = 0;
        dp[0][1] = nums[0];//初始化动态规划数组初始条件
        for(int i = 1; i < n; i++)
        {
            dp[i][0] = max(dp[i-1][0], dp[i-1][1]);
            dp[i][1] = dp[i-1][0] + nums[i];
        }
        return max(dp[n-1][0], dp[n-1][1]);
    }
};

动态规划优化(滚动数组)

同理,可以大大优化,不使用数组的实现方式,而仅仅使用两个变量dp0和dp1进行n-1次迭代即可。

 

class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        int dp0 = 0;
        int dp1 = nums[0];//动态规划初始条件
        for(int i = 1; i < n; i++)
        {
            int newDp0 = max(dp0, dp1);
            dp1 = dp0 + nums[i];
            dp0 = newDp0;
        }
        return max(dp0, dp1);
    }
};

三、官方题解(来源:力扣(LeetCode))

方法:动态规划(另一种动态规划思路)

首先考虑最简单的情况。如果只有一间房屋,则偷窃该房屋,可以偷窃到最高总金额。如果只有两间房屋,则由于两间房屋相邻,不能同时偷窃,只能偷窃其中的一间房屋,因此选择其中金额较高的房屋进行偷窃,可以偷窃到最高总金额。

如果房屋数量大于两间,应该如何计算能够偷窃到的最高总金额呢?对于第 k (k>2) 间房屋,有两个选项:

  1. 偷窃第 k 间房屋,那么就不能偷窃第 k−1 间房屋,偷窃总金额为前 k−2 间房屋的最高总金额与第 k 间房屋的金额之和。
  2. 不偷窃第 k 间房屋,偷窃总金额为前 k−1 间房屋的最高总金额。

在两个选项中选择偷窃总金额较大的选项,该选项对应的偷窃总金额即为前 k 间房屋能偷窃到的最高总金额。

用 dp[i] 表示前 i 间房屋能偷窃到的最高总金额,那么就有如下的状态转移方程:

dp[i] = max(dp[i-2]+nums[i], dp[i-1])

边界条件为:dp[0] = nums[0], dp[1] = max(nums[0], nums[1])

最终答案即为dp[n-1],n为数组长度

class Solution {
public:
    int rob(vector<int>& nums) {
        if (nums.empty()) {
            return 0;
        }
        int size = nums.size();
        if (size == 1) {
            return nums[0];
        }
        vector<int> dp = vector<int>(size, 0);
        dp[0] = nums[0];
        dp[1] = max(nums[0], nums[1]);
        for (int i = 2; i < size; i++) {
            dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
        }
        return dp[size - 1];
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/house-robber/solution/da-jia-jie-she-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

上述方法使用了数组存储结果。考虑到每间房屋的最高总金额只和该房屋的前两间房屋的最高总金额相关,因此可以使用滚动数组,在每个时刻只需要存储前两间房屋的最高总金额。

class Solution {
public:
    int rob(vector<int>& nums) {
        if (nums.empty()) {
            return 0;
        }
        int size = nums.size();
        if (size == 1) {
            return nums[0];
        }
        int first = nums[0], second = max(nums[0], nums[1]);
        for (int i = 2; i < size; i++) {
            int temp = second;
            second = max(first + nums[i], second);
            first = temp;
        }
        return second;
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/house-robber/solution/da-jia-jie-she-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

复杂度分析

  • 时间复杂度:O(n),其中 n 是数组长度。只需要对数组遍历一次。
  • 空间复杂度:O(1)。使用滚动数组,可以只存储前两间房屋的最高总金额,而不需要存储整个数组的结果,因此空间复杂度是 O(1)。

四、学习心得

①对于本题动态规划状态转移方程的两种思路:

(1)dp[i][0]表示经过第i家(i从0计)后选择不偷该家的钱,;dp[i][1]表示经过第i家后选择不偷该家的钱。

dp[i][0] = max(dp[i-1][0], dp[i-1][1])

dp[i][1] = dp[i-1][0] + nums[i]

(2)dp[i] 表示前 i 间房屋能偷窃到的最高总金额

dp[i] = max(dp[i-2]+nums[i], dp[i-1])

数组数据结构问题涉及到多种情况并且存在先后的递推关系时可以首先考虑动态规划

滚动数组的动态规划优化方案可以将空间复杂度从O(n)优化至O(1)

标签:偷窃,nums,int,37,力扣,房屋,数组,打家劫舍,dp
来源: https://blog.csdn.net/micah_yaokunkun/article/details/122757034

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有