这个就和爬楼梯很像,只不过这个每次只能走两步,求最后偷到钱的最大价值。
动规五部曲:
- dpi代表到下标i的家里时,考虑偷还是不偷的最大价值,这里的考虑非常重要,如果偷下标i的房间,那么dpi=dpi-2+numsi。如果不偷,那就是dpi=dpi-1;因为偷了当前房间,i-1房间就不能偷了。
- 所以递推公式:dpi=max(dpi-2+numsi,dpi-1);
- 初始化,dp0=nums0,dp1=max(nums0,nums1)
- 遍历顺序,从前往后
java
class Solution {
public int rob(int[] nums) {
if(nums.length==1){
return nums[0];
}
int[] dp=new int[nums.length];
dp[0]=nums[0];
dp[1]=Math.max(nums[0],nums[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];
}
}
这里数组变成了环形的,首位相连。头节点和尾结点不能同时取。那就进行分类,分成只取头结点时所能偷的最大钱数和只取尾结点时所能偷的最大钱数。
- dpi表示到下标为i的房间时,考虑偷还是不偷所能得到的最大钱数为dpi
- 递推公式:dpi=max(dpi-2+numsi,dpi-1),两种情况的递推公式都是一样的,只是在初始化的时候和遍历的时候有区别
- 初始化:只考虑头结点:dp10=nums0,dp11=max(nums0,nums1);只考虑尾结点:dp21=nums1,dp22=max(nums1,nums2);
- 遍历顺序:只考虑头结点时,从下标0开始遍历到nums.length-2,只考虑尾结点时,从下标1开始遍历到nums.length-1
java
class Solution {
public int rob(int[] nums) {
if(nums.length==1){
return nums[0];
}
if(nums.length==2){
return Math.max(nums[0],nums[1]);
}
int[] dp1=new int[nums.length-1];//考虑首元素
int[] dp2=new int[nums.length];//考虑尾元素
dp1[0]=nums[0];
dp2[0]=0;
dp1[1]=Math.max(nums[0],nums[1]);
dp2[1]=nums[1];
dp2[2]=Math.max(nums[1],nums[2]);
for(int i=2;i<nums.length-1;i++){
dp1[i]=Math.max(dp1[i-2]+nums[i],dp1[i-1]);
}
for(int i=3;i<nums.length;i++){
dp2[i]=Math.max(dp2[i-2]+nums[i],dp2[i-1]);
}
return Math.max(dp1[nums.length-2],dp2[nums.length-1]);
}
}
这是将数组改成了二叉树,这是树形dp的入门题。既然是二叉树,就要考虑使用什么遍历方式,递归和递归三部曲。这里必须使用后序遍历,因为要先将两个孩子上的最大钱数算出来,然后去当前节点偷还是不偷进行比较,如果不偷当前节点,那就偷孩子节点。
动规五部曲:
- dpi代表到节点i时,偷当前节点的最大钱数为dp1,不偷当前节点的最大钱数为dp0
- 递推公式:偷当前节点:val1=cur.val+leftdp0+rightdp0;不偷当前节点:val2=max(leftdp1,leftdp0)+max(rightdp1,rightdp0);最终返回值为{不偷当前节点得到的最大金钱,偷当前节点得到的最大金钱}
- 初始化:递归不用初始化
- 遍历顺序:按照递归
递归三部曲:
- 确定参数和返回值:参数就为根节点,返回值为根节点的{不偷当前节点得到的最大金钱,偷当前节点得到的最大金钱}
- 终止条件:当遍历到叶子节点后,直接返回{0,0};
- 单层处理逻辑:就是偷当前节点和不偷当前节点的最大钱数
java
class Solution {
public int rob(TreeNode root) {
int[] dp=robby(root);
return Math.max(dp[0],dp[1]);
}
public int[] robby(TreeNode cur){
if(cur==null){
return new int[]{0,0};
}
int[] leftdp=robby(cur.left);
int[] rightdp=robby(cur.right);
//偷当前节点=当前节点价值+不偷左右子节点
int val1=cur.val+leftdp[0]+rightdp[0];
//不偷当前节点=左右子节点偷与不偷的最大价值
int val2=Math.max(leftdp[0],leftdp[1])+Math.max(rightdp[0],rightdp[1]);
return new int[]{val2,val1};
}
}