代码sxl有详细过程,这里只做强调
1.确定 dp[i] 中 i 的含义也就是背包的容量,一开始想确定为就是所有金额的和sum,后来觉得多此一举,既然就选不选两种,然后就以nums的下标作为 i 的含义,并且将题目给的数组求长度作为背包的容量也是常见的
2.递推公式可以说比较巧妙,但也很好理解,就取不取两种取最大就好,一开始还想拿一个bool还确定取没取,后来发现不需要,交给前一个状态就好
3.初始化 dp[0] dp[1] 是递归入口,其他不初始化都可以,边界按含义就好!
4.提前判断少不了
class Solution {
public:
int rob(vector<int>& nums) {
//1.
int len=nums.size();
if(len==0) return 0;
if(len==1) return nums[0];
//2.
vector<int> dp(len);
dp[0]=nums[0];
dp[1]=max(nums[0],nums[1]);
//3.
for(int i=2;i<len;i++){
dp[i]=max(dp[i-2]+nums[i],dp[i-1]);
}
//4.
return dp[len-1];
}
};
Java版本
class Solution {
public int rob(int[] nums) {
//1.
int len = nums.length;
int[] dp = new int[len];
if(len==0) return 0;
if(len==1) return nums[0];
if(len==2) return Math.max(nums[0], nums[1]);
dp[0] = nums[0];
dp[1] = Math.max(nums[0], nums[1]);
//2.
for(int i=2;i<len;i++){
dp[i] = Math.max(dp[i-1], dp[i-2]+nums[i]);
}
return dp[len-1];
}
}
代码sxl上面有详细过程,这只做强调
1.由于题目特殊性,无法直接动态规划,所以我们要通过分析然后转化成198那种普通的打家劫社,于是既然首尾不能同时出现,那就分情况讨论,一种不出现首,一种不出现尾,然后一合大的就是结果,分情况后和198一摸一样
2.可能是边上课边写的代码,一个很粗心的错误如下,就是调用simpleRob函数后,如果定义了len=end-start+1,那么start还是对应nums,你这开辟了一个新的,dp的下标和nums的下标就不一致了
//int len=end-start+1;
int len=nums.size();
//2.
vector<int> dp(len);
class Solution {
public:
int rob(vector<int>& nums) {
//1.
int len=nums.size();
if(len==0) return 0;
if(len==1) return nums.at(0);
//2.闭区间 [start,end]
int result1=simpleROb(nums,0,len-2);
int result2=simpleROb(nums,1,len-1);
//3.
return max(result1,result2);
}
//和198一样的思路
int simpleROb(vector<int>& nums,int start,int end){
//1.
if(start==end) return nums.at(start);
//int len=end-start+1;
int len=nums.size();
//2.
vector<int> dp(len);
dp[start]=nums.at(start);
dp[start+1]=max(nums.at(start),nums.at(start+1));
//3.
for(int i=start+2;i<=end;i++){
dp[i]=max(dp[i-1],dp[i-2]+nums.at(i));
}
//4.
return dp[end];
}
};
Java版本,注意点:
1.只有当len==4的时候才需要进入dp,其他情况答案都是固定的
2.在dp中,有一个映射的关系 dp[0]对应的是nums[left],所以这个要关注
class Solution {
public int rob(int[] nums) {
int len = nums.length;
if(len==0) return 0;
if(len==1) return nums[0];
if(len==2) return Math.max(nums[0], nums[1]);
if(len==3) return Math.max(nums[0], Math.max(nums[1], nums[2]));
int ans1 = robMax(nums, 0, len-2);
int ans2 = robMax(nums, 1, len-1);
return Math.max(ans1, ans2);
}
public int robMax(int[] nums, int left, int right) {
//1.
//0,1,2 len>=3
int len = right-left+1;
int[] dp = new int[len];
dp[0] = nums[left];
dp[1] = Math.max(nums[left], nums[left+1]);
//2.
for(int i=2;i<len;i++){
dp[i] = Math.max(dp[i-1], dp[i-2]+nums[left+i]);
}
return dp[len-1];
}
}
代码sxl上面有详细解答,这里制作强调
1.这个题是递归三部曲和动态规划五部曲的结合
2.暴力递归和记忆化递推都是实时计算,没有做记录
3.使用一个二元组对应每一个节点的状态,即不偷和偷的最大值,并且遍历的顺序也是后序遍历,也就是说计算当前节点的二元组时它的左右儿子都已经计算过了,所以也是自底向上,很符合动态规划的思想!
4.至于递推比较简单
5.初始化即叶节点的左右儿子状态都是 {0,0 }
class Solution {
public:
vector<int> treeRob(TreeNode* cur){
//1.递归出口
if(cur== nullptr) return vector<int>{0,0};
//2.
vector<int> left=treeRob(cur->left);
vector<int> right=treeRob(cur->right);
//3.动态规划状态转移
int notROb=max(left[0],left[1])+max(right[0],right[1]);
int ROb=cur->val+left[0]+right[0];
//4.
return vector<int>{notROb,ROb};
}
int rob(TreeNode* root) {
//ans[0]不偷 ans[1]偷
vector<int> ans=treeRob(root);
return max(ans[0],ans[1]);
}
};