动态规划—#198. 打家劫舍
- 前言
- 题目描述
- 基本思路
- 1. 问题定义:
- 2. 理解问题和递推关系:
- 3. 解决方法:
- 4. 进一步优化:
- 5. 小总结:
- 代码实现
- Python3代码实现
- Python 代码解释
- C++代码实现
- C++ 代码解释
- 总结:
前言
在这个问题中,你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
题目描述
基本思路
1. 问题定义:
你是一个小偷,计划偷窃沿街的房屋,每个房屋里都有一定数量的现金。由于房屋之间装有警报器,如果你偷了相邻的两家,那么警报就会响。因此,你需要设计一个策略,确保不触发警报器的情况下,偷到最多的现金。
给定一个数组 n u m s [ ] nums[] nums[],其中 n u m s [ i ] nums[i] nums[i] 表示第 i i i 个房屋中的现金数,你需要返回你在不触发警报的前提下能偷窃到的最高金额。
2. 理解问题和递推关系:
- 对于每一个房子,你有两种选择:
- 偷这个房子,那么你就不能偷前一个房子,只能考虑前 i − 2 i-2 i−2 个房子的收益。
- 不偷这个房子,那么你可以选择偷或不偷前一个房子的最大收益。
- 这给了我们一个递推关系式:
d p [ i ] = max ( d p [ i − 1 ] , d p [ i − 2 ] + nums [ i ] ) d p[i]=\max (d p[i-1], d p[i-2]+\operatorname{nums}[i]) dp[i]=max(dp[i−1],dp[i−2]+nums[i])
其中 d p [ i ] dp[i] dp[i] 表示偷到第 i i i 个房子时的最大金额。
3. 解决方法:
- 初始条件:如果只有一个房子,那么结果是 n u m s [ 0 ] nums[0] nums[0];如果有两个房子,结果是 m a x ( n u m s [ 0 ] , n u m s [ 1 ] ) max(nums[0], nums[1]) max(nums[0],nums[1])。
- 递推公式:对于第 i i i 个房子,偷或不偷的最大金额为 d p [ i ] = max ( d p [ i − 1 ] , d p [ i − 2 ] + d p[i]=\max (d p[i-1], d p[i-2]+ dp[i]=max(dp[i−1],dp[i−2]+ nums[i])。
- 目标:计算出最后一个房子 d p [ n − 1 ] d p[n-1] dp[n−1] ,即偷窃到最后一个房子的最大金额。
4. 进一步优化:
在递推过程中,我们注意到每次计算 d p [ i ] d p[i] dp[i] 只依赖于 d p [ i − 1 ] d p[i-1] dp[i−1] 和 d p [ i − 2 ] d p[i-2] dp[i−2] ,因此我们可以使用两个变量来代替整个 d p [ ] d p[] dp[] 数组,节省空间。
- 时间复杂度: O ( n ) O(n) O(n) ,只需要遍历一次数组。
- 空间复杂度: O ( 1 ) O(1) O(1) ,只使用常量空间。
5. 小总结:
- 递推思路:每个房子有偷或不偷两种选择,偷当前房子的话要加上前 i − 2 i-2 i−2 个房子的收益,不偷的话则直接沿用前一个房子的最大收益。
- 优化:通过只使用两个变量存储前面的结果,可以将空间复杂度优化为 O ( 1 ) O(1) O(1) 。
- 目标是找到通过递推公式,偷窃到最后一个房子的最大金额。
以上就是打家劫舍问题的基本思路。
代码实现
Python3代码实现
class Solution:
def rob(self, nums: list[int]) -> int:
# 边界情况处理:如果房子数量为0或1
if not nums:
return 0
elif len(nums) == 1:
return nums[0]
# 初始化前两个房子的最大收益
prev2 = 0 # 表示前两个房子的收益
prev1 = nums[0] # 表示前一个房子的收益
# 从第三个房子开始计算到最后一个房子
for i in range(1, len(nums)):
# 当前房子的最大收益为:偷前两个房子加当前房子的收益,或者不偷这个房子,延续前一个房子的收益
current = max(prev1, prev2 + nums[i])
# 更新前两个房子的最大收益
prev2 = prev1
prev1 = current
# 最终返回偷窃到最后一个房子的最大收益
return prev1
Python 代码解释
- Base Case:处理只有一个房子或者没有房子的情况。
- 动态规划:使用 p r e v 2 prev2 prev2 和 p r e v 1 prev1 prev1 来存储偷前两个房子和前一个房子的最大收益,然后依次更新。
- 最终结果:返回最后一个房子的最大收益。
C++代码实现
class Solution {
public:
int rob(vector<int>& nums) {
// 边界情况处理:如果房子数量为0或1
if (nums.empty()) return 0;
if (nums.size() == 1) return nums[0];
// 初始化前两个房子的最大收益
int prev2 = 0; // 表示前两个房子的收益
int prev1 = nums[0]; // 表示前一个房子的收益
// 从第三个房子开始计算到最后一个房子
for (int i = 1; i < nums.size(); ++i) {
// 当前房子的最大收益为:偷前两个房子加当前房子的收益,或者不偷这个房子,延续前一个房子的收益
int current = max(prev1, prev2 + nums[i]);
// 更新前两个房子的最大收益
prev2 = prev1;
prev1 = current;
}
// 最终返回偷窃到最后一个房子的最大收益
return prev1;
}
};
C++ 代码解释
- Base Case:处理只有一个房子或者没有房子的情况。
- 动态规划:使用 prev2 和 prev1 变量,依次更新每个房子偷窃的最大收益。
- 最终结果:返回最后一个房子的最大收益。
总结:
- 递推公式: d p [ i ] = max ( d p [ i − 1 ] , d p [ i − 2 ] + d p[i]=\max (d p[i-1], d p[i-2]+ dp[i]=max(dp[i−1],dp[i−2]+ nums[i]),通过选择偷或不偷当前房子,来最大化收益。
- 空间优化:将空间复杂度优化为 O ( 1 ) O(1) O(1) ,通过两个变量 prev1 和 prev2 存储前两个房子的最大收益。
- 时间复杂度: O ( n ) O(n) O(n) ,只需遍历一次数组。