【DP】LeetCode 198. 打家劫舍
题目链接
思路
分析动态规划题目的时候只需要考虑最后一个阶段,因为所有的阶段转化都是相同的,考虑最后一个阶段容易发现规律
在数组的动态规划问题中,一般 dp[i]
都是表示以 nums
以前 i 个元素组成(即 nums[i - 1]
)的状态;dp[i][j]
分别表示以 nums1
前 i 个元素(即 nums1[i - 1]
)组成和以 nums2
前 j 个元素(即 nums2[j - 1]
)组成的状态,以此类推
字符串也是个数组,是字符数组
表示状态
状态表示就是靠猜,但是会有猜的套路,一般都是通过最终结果和数组数量来猜
这是一道非常经典的动态规划题目
我们定义 \(dp[i]\) 为打劫前 i 个房屋所能获得的最大金额
找状态转移方程
思考的方向是:大问题的最优解怎么由小问题的最优解得到
当我们打劫到第 i 个房屋的时候,有两个策略:
- 打劫第 i - 1 个房屋,跳过第 i 个房屋
- 打劫第 i 个房屋,跳过第 i - 1个房屋
二者取最大值,能得到状态转移方程:
\[dp[i] = \max(dp[i - 1], dp[i - 2] + nums[i - 1]);
\]
边界处理
\[dp[1] = nums[0]
\]
空间优化
使用三个变量 \(pre1\)、\(pre2\) 和 \(current\) 分别表示 \(dp[i - 2]\)、\(dp[i - 1]\) 和 \(dp[i]\),循环滚动更新
代码
dp
数组版
class Solution {
public int rob(int[] nums) {
int n = nums.length;
// dp[i] 表示前 i 个房屋能打劫到的金额
int[] dp = new int[n + 1];
dp[1] = nums[0];
for(int i = 2; i <= n; i++){
// 不选 nums[i - 1]和选 nums[i - 1] 之间取最大值
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i - 1]);
}
return dp[n];
}
}
空间优化版
class Solution {
public int rob(int[] nums) {
int n = nums.length;
// dp[i] 表示前 i 个房屋能打劫到的金额
// pre1 表示 dp[i - 2]
int pre1 = 0;
// pre2 表示 dp[i - 1]
int pre2 = nums[0];
int current = 0;
for(int i = 2; i <= n; i++){
// 不选 nums[i - 1]和选 nums[i - 1] 之间取最大值
current = Math.max(pre2, pre1 + nums[i - 1]);
pre1 = pre2;
pre2 = current;
}
return pre2;
}
}