随想录训练营Day48 | DP - - 198.打家劫舍, 213.打家劫舍II,337.打家劫舍 III
标签: LeetCode闯关记
198.打家劫舍
class Solution {
public int rob(int[] nums) {
int[] dp = new int[nums.length];//dp[i]表示考虑下标i(包括i)以内的房屋,最多可以偷窃的金额为dp[i]
dp[0] = nums[0];
if(nums.length == 1){
return dp[0];
}
dp[1] = Math.max(nums[0],nums[1]);
if(nums.length == 2){
return dp[1];
}
for (int i = 2; i < nums.length; i++) {
dp[i] = Math.max(dp[i-2] + nums[i], dp[i-1]);
}
return dp[nums.length-1];
}
}
疑惑点(尽管解释了还是有点晕)
- 打劫问题在考虑
dp[i]偷或者不偷的时候,为什么没有考虑dp[i-1]是否偷了?- 在这个问题中,我们使用DP状态数组来记录偷取前i个房子的最大收益。
- 其中dp[i]表示考虑到第i个房子时的最大收益。
- 在状态转移方程中,我们需要决定是否偷取第i个房子。
- 如果我们选择偷取第i个房子,那么前一个偷取的房子就一定是第i-2个房子,因为相邻的两个房子不能同时被偷取。
- 如果我们不偷取第i个房子,那么前一个偷取的房子可以是
第i-1个房子或者是第i-2个房子。因此,在状态转移方程中我们比较的是dp[i-1]和dp[i-2]+nums[i]的大小。- 即,在每一个房子,只考虑两种情况——偷或者不偷,不考虑前面房子是否被偷的影响。只要面对第i个房子做出选择就好。
213.打家劫舍II
class Solution {
public int rob(int[] nums) {
int len = nums.length;
if(len == 1){//特殊情况:测试用例:[0]
return nums[0];
}
return Math.max(robAction(nums,0,len-1),robAction(nums,1,len));
}
private int robAction(int[] nums, int start, int end){//左闭右开
int dp_size = end-start;
int[] dp = new int[dp_size];//dp[i]表示考虑下标i(包括i)以内的房屋,最多可以偷窃的金额为dp[i]
dp[0] = nums[start];
if(dp_size == 1){
return dp[0];
}
dp[1] = Math.max(nums[start],nums[start + 1]);
if(dp_size == 2){
return dp[1];
}
for (int i = 2; i < dp_size; i++) {
dp[i] = Math.max(dp[i-2] + nums[i+start], dp[i-1]);//key:nums[i+start]
}
return dp[dp_size - 1];
/* 这样更简单(可是还是不太懂这个顺序以及为什么不能改变)
在下一次迭代之前,需要保存上一个位置抢劫到的最大金额,那么用变量 y 保存变量 z 的旧值。
变量 z根据递推公式变化, 表示当前位置抢劫到的最大金额。
x实际上保存的是上一个迭代中变量y的值,也就是z的前一个值。
int robAction(int[] nums, int start, int end) {
int x = 0, y = 0, z = 0;
for (int i = start; i < end; i++) {
y = z;
z = Math.max(y, x + nums[i]);
x = y;
}
return z;
}
*/
}
}
337.打家劫舍 III
class Solution {
public int rob(TreeNode root) {
int[] result = robAction(root);
return Math.max(result[0],result[1]);
}
public int[] robAction(TreeNode root){
int[] res = new int[2];
if(root == null){
return res;
}
int[] left = robAction(root.left);//左
int[] right = robAction(root.right);//右
//中
res[0] = Math.max(left[0],left[1]) + Math.max(right[0],right[1]);//不偷当前节点
res[1] = root.val + left[0] + right[0];//偷当前节点
return res;
}
}