引言
今天做题跳跃一点,开始动态规划专题。
打家劫舍
- 🎈 题目链接:
- 🎈 做题状态:
我的解题
这道题是一个典型的dp问题。也就是说后面的状态可以由前面的状态递归而来。首先回顾一下题目的要求,求解小偷沿屋偷窃完之后最大能盗取的金额。
动态规划的几个关键步骤如下:
- 定义dp数组,本题的dp[i]表示偷取 0 到 i-1 家房屋后最大能偷取的金额值。dp[0] 表示不偷取, dp[1]表示的是偷取 nums[0] 家金额的最大金额。 所以dp的大小是 nums.size() + 1.
- 状态转移方程,也就是状态推导公式。 dp[i] 表示偷取到 nums[i-1]家时的最大盗取金额,那么这个有两种情况,一种是偷取 nums[i-1] 家的钱,一种就是不偷取 nums[i-1] 家的钱。然后我们需要取这两种状态的最大值,也就是
dp[i + 1] = max(dp[i], dp[i-1] + nums[i]);
dp[i+1] 如果不偷取 nums[i] 那么就是 dp[i] ,如果偷取的话,就需要取 dp[i-1] + nums[i] 这个值。 - 初始化dp数组,肯定要出事两个dp,也就是dp[0] 和 dp[1] 这两个
- 遍历顺序,本题只需要顺序遍历即可。
class Solution {
public:
int rob(vector<int>& nums) {
vector<int> dp(nums.size() + 1);
dp[0] = 0;
dp[1] = nums[0];
for (int i = 1; i < nums.size(); ++i)
{
dp[i + 1] = max(dp[i], dp[i-1] + nums[i]);
}
return dp[nums.size()];
}
};
代码优化
主要是优化了代码空间,因为当前状态主要由前两个状态决定,所以只需要两个变量来记录前两个状态即可。
class Solution {
public:
int rob(vector<int>& nums) {
int prev = 0, curr = 0;
for (int num : nums) {
int temp = max(curr, prev + num);
prev = curr;
curr = temp;
}
return curr;
}
};
解法思路:
使用动态规划,通过两个变量 prev
和 curr
分别记录前前一个和当前的最大金额。每次遍历到一个新房屋时,计算偷或不偷该房屋的较大值,并更新这两个变量。这样既保证了时间复杂度为 O(n),空间复杂度为 O(1)。
步骤解析:
- 初始化:
prev
和curr
初始均为 0,表示没有房屋时的最大金额。 - 遍历每个房屋:
- 计算当前房屋偷与不偷的最大值:
temp = max(curr, prev + num)
。 - 更新
prev
为之前的curr
,curr
为新的最大值。
- 计算当前房屋偷与不偷的最大值:
- 返回结果: 遍历结束后,
curr
即为最终的最大金额。
复杂度分析:
- 时间复杂度: O(n),只需遍历一次数组。
- 空间复杂度: O(1),仅使用两个额外变量。