背包问题总结:
一、打家劫舍
(一)dp数组含义:第i个元素前能得到的最大钱数
(二)递推公式:dp[i]=max(dp[i-1](不偷i),dp[i-2]+nums[i](偷i))
(三)初始化:
dp[0]=nums[0]
dp[1]=max(nums[0],nums[1]))
(四)遍历顺序:从前往后
注意如果nums数组只有一个甚至没有元素要提前返回0,nums[0],防止后边遍历的时候下标越界
class Solution {
public:
int rob(vector<int>& nums) {
if(nums.size()==0)return 0;
if(nums.size()==1)return nums[0];
vector<int>dp(nums.size(),0);
dp[0]=nums[0];
dp[1]=max(nums[0],nums[1]);
for(int i=2;i<nums.size();i++){
dp[i]=max(dp[i-1],dp[i-2]+nums[i]);
}
return dp[nums.size()-1];
}
};
二、打家劫舍Ⅱ
原理同上。但是需要注意这题把环拆成两个线性数组的思路
class Solution {
public:
int maxvalue(vector<int>&nums){
if(nums.size()==0)return 0;
if(nums.size()==1)return nums[0];
vector<int>dp(nums.size(),0);
dp[0]=nums[0];
dp[1]=max(nums[0],nums[1]);
for(int i=2;i<nums.size();i++){
dp[i]=max(dp[i-1],dp[i-2]+nums[i]);
}
return dp[nums.size()-1];
}
int rob(vector<int>& nums) {
if(nums.size()==1)return nums[0];
if(nums.size()==0)return 0;
vector<int>front(nums.begin(),nums.end()-1);
vector<int>back(nums.begin()+1,nums.end());
return max(maxvalue(front),maxvalue(back));
}
};
三、打家劫舍Ⅲ
第一次接触树形dp。有点复杂,也不算特别难。问题的关键是这题用dp数组控制的是本节点偷与不偷各自的钱数,与以往dp数组把握全局不同,这题由于递归函数入栈出栈,每经历一个节点就会 有一个新的dp[0] dp[1]用于存放这之前的所有数据,不需要特别长的长数组也可以实现更新的操作
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int rob(TreeNode* root) {
vector<int>result=robtree(root);
return max(result[0],result[1]);
}
vector<int>robtree(TreeNode*cur){
if(cur==nullptr){
return {0,0};
}
vector<int>left=robtree(cur->left);
vector<int>right=robtree(cur->right);
int isstolen=cur->val+left[0]+right[0];
int nostolen=max(left[0],left[1])+max(right[0],right[1]);
vector<int>result={nostolen,isstolen};
return result;
}
};
易错点是在下面递归函数返回的时候,要把nostolen放前面与dp[0]对应,表示没偷的状态