代码随想录算法训练营第四十八天|198.打家劫舍、213.打家劫舍II、337.打家劫舍III
198.打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
题解:因为偷东西要考虑前两家偷的状态,所以用动态规划来解决这个问题
- dp[i] :包含 i 在内的几家中能偷到的最大金额是 dp[i]
- 递推公式: dp[j]=Math.max(dp[j-2]+nums[i] (偷), dp[j-1] (不偷))
- 初始化:dp[0] = nums[0] (只有一家的话是要偷的) dp[1]=max(nums[0],nums[1]) 有两家的话偷金额大的那家
- 遍历顺序:从前往后
- 打印dp数组
代码:
class Solution {
public int rob(int[] nums) {
if(nums==null||nums.length==0){
return 0;
}
if(nums.length==1) return nums[0];
int n=nums.length;
int [] dp=new int[n];
dp[0]=nums[0];
dp[1]=Math.max(dp[0],nums[1]);
for(int i=2;i<n;i++){
dp[i]=Math.max(dp[i-2]+nums[i],dp[i-1]);
}
return dp[n-1];
}
}
213.打家劫舍II
是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
示例 1:
输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
题解:与I相比就是连成环的问题,那么如果取了首尾就不能同时取了。将环的问题转换成线性问题,分别考虑只取首或尾的情况,然后取两者的最大值即可。
- dp[i] :包含下标 i 的房子以及前面的房子所能偷到的最大金额
- 递推公式:dp[i]=Math.max(dp[i]+nums[i] ,dp[i-1])
- 初始化:dp[0]=nums[0] dp[1]=Math.max(dp[0],nums[1])
- 遍历方式:从前向后
- 打印dp数组
代码:
class Solution {
public int rob(int[] nums) {
if(nums==null || nums.length==0){
return 0;
}
if(nums.length==1) return nums[0];
int res1=first(nums,0,nums.length-2);
int res2=first(nums,1,nums.length-1);
return Math.max(res1,res2);
}
//本来有点疑惑dp大小的问题,后来反应过来大小没关系,反正没用到的是会初始化为0的
public int first(int [] nums,int start,int end){
if(start==end) return nums[start];
int dp[]=new int[nums.length];
dp[start]=nums[start];
dp[start+1]=Math.max(nums[start],nums[start+1]);
for(int i=start+2;i<=end;i++){
dp[i]=Math.max(dp[i-1],dp[i-2]+nums[i]);
}
return dp[end];
}
}
337.打家劫舍III
小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root
。
除了 root
之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。
给定二叉树的 root
。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。
示例 1:
输入: root = [3,2,3,null,3,null,1]
输出: 7
解释: 小偷一晚能够盗取的最高金额 3 + 3 + 1 = 7
题解:这个题和装监控那个题有点相似啊。但是呢这个题要考虑节点值的大小,并且返回的参数有点问题。
哦,树形 dp。每个节点都有两种状态,偷和不偷,用dp[0]表示不偷,dp[1]表示偷,那么dp数组的大小就为2.
-
dp[i] :如果i 为0表示不偷,i为1表示偷
-
递推公式:不偷:val1=Math.max(left[0],left[1]+Math.max(right[0],right[]1)) (看看孩子节点要不要偷)
偷:val2=curr.val+left[0]+right[0] ,偷了当前节点就不能偷孩子节点
-
初始化:dp[0][0 ,没开始价值为0
-
遍历顺序:后序遍历,从叶子节点开始,因为涉及到计算问题
-
打印dp数组
代码:
class Solution {
public int rob(TreeNode root) {
int [] res=digui(root);
return Math.max(res[0],res[1]);
}
public int[] digui(TreeNode curr){
int dp[]=new int[2];
if(curr==null) return dp;
int [] left=digui(curr.left); //用来装左右孩子偷和不偷的值
int [] right=digui(curr.right);
//不偷
dp[0]=Math.max(left[0],left[1])+Math.max(right[0],right[1]);
//偷,那孩子节点就不能偷了
dp[1]=curr.val+left[0]+right[0];
return dp;
}
}