1.题目链接:
198. 打家劫舍 - 力扣(LeetCode)
2.题目描述:
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 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
3.解题思路:
该问题采用动态规划解决,核心状态转移方程为:
dp[i]=max(dp[i−1],dp[i−2]+nums[i])
dp[i] 表示前 i个房屋的最大偷窃金额
dp[i−1] 表示不偷第 i 间房屋时的最大收益
dp[i−2]+nums[i] 表示偷第 i 间房屋时的收益(需跳过前一间)
通过空间优化,代码用三个变量替代完整的 DP 数组:
-
pas
:存储 dp[i−2](前两间的最优解) -
pre
:存储 dp[i−1](前一间的最优解) -
tem
:临时保存状态(用于更新)
我们采用了动态规划的思路,通过不断更新每间房屋可以偷盗的最大金额,来解决“打家劫舍”问题。首先,判断如果没有房屋,则返回 0;如果只有一间房屋,则返回该房屋的金额。然后,定义两个变量 pas 和 pre,分别代表第 i-2 间房屋和第 i-1 间房屋的最大偷盗金额,并初始化这两个变量。接着,从第三间房屋开始,逐步计算每一间房屋的最大偷盗金额。对于每间房屋,判断偷或者不偷当前房屋,通过 pre 和 pas 来计算当前最大偷盗金额。具体来说,偷当前房屋时的金额是 pas + nums[i](即前两间房屋的偷盗金额之和);不偷当前房屋时,金额是 pre。更新 pre 和 pas,直到遍历完所有房屋。最终返回 pre,即最大偷盗金额。
4.题解代码:
class Solution {
public:int rob(vector<int>& nums){if (nums.empty())return 0;//如果没有房屋,则偷盗金额返回0if (nums.size() == 1)return nums[0];//如果只有一间房屋,则偷盗金额返回这间房屋的金额int pas = nums[0];//定义一个pas,代表第 i-2 间房屋的最大偷窃金额,初始化为第一间房屋的偷盗金额int pre = max(nums[0], nums[1]);//定义一个pre,代表第 i-1 间房屋的最大偷窃金额,初始化为前两间房屋的最大值int tem = 0;//定义一个tem,作为临时值储存当前prefor (int i = 2; i < nums.size(); i++){tem = pre;//储存pre的值pre = max(pre, pas + nums[i]); //选择偷或者不偷当前房屋pas = tem;//将pas更新为之前的pre}return pre;//返回最大偷盗金额}
};
5.示例演算:
输入数组:[2, 7, 9, 3, 1]
步骤 | 当前房屋(i) | nums[i] | 操作 | pas 值 | pre 值 | 计算逻辑 |
---|---|---|---|---|---|---|
初始 | - | - | - | 2 | 7 | - |
1 | 2 | 9 | tem = pre pre = max(7, 2+9) pas = tem | 7 | 11 | max(7,2+9)=11 |
2 | 3 | 3 | tem = pre pre = max(11, 7+3) pas = tem | 11 | 11 | max(11,7+3)=11 |
3 | 4 | 1 | tem = pre pre = max(11, 11+1) pas = tem | 11 | 12 | max(11,11+1)=12 |
6.复杂度计算:
时间复杂度:它通过一次遍历处理每个房屋的金额,故时间复杂度是O(n)
空间复杂度:只使用了常数数量的额外变量,故空间复杂度为O(1)