198.打家劫舍
1.确定dp数组的含义
dp[i]:考虑到下标为i和之前的房屋最多可以偷窃的金额是dp[i]
2.确定递推公式
因为相邻的房屋不能偷,所以我第i个的dp值就是考虑我偷当前的房间加上前两个的房间的值和我只偷前一个房间的值进行比较选最大值
递推公式:dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
敲代码的时候我是按下面这个来敲的,答案是错的,感觉就是因为这里的dp数组是考虑进了之前的房屋的值的,但是我直接加nums的值的话是不会考虑进去的,只有单拎的值
//递推公式错误代码
for(int i=2;i<nums.size();i++){
dp[i]=max(nums[i]+nums[i-2],nums[i-1]);
}
3.dp数组初始化
如果要使用递推公式考虑,要使用前一个和前两个数组的值,从0开始可能会超限,所以直接把dp[0]赋值nums[0],dp[1]赋值nums[0]和nums[1]的最大值
4.遍历顺序
dp[i]是由前两个推导出来的,所以从前往后进行遍历
5.(打印dp数组)
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());
dp[0]=nums[0];
dp[1]=max(nums[0],nums[1]);
for(int i=2;i<nums.size();i++){
dp[i]=max(nums[i]+dp[i-2],dp[i-1]);
}
return dp[nums.size()-1];
}
};
213.打家劫舍||
题目链接:213. 打家劫舍 II - 力扣(LeetCode)
把成环的展开可以考虑不包含尾元素的情况
以及不包含首元素的情况
这里就是把上一道题封装成函数,两种情况分别进行计算,看谁的金额高,然后返回
class Solution {
public:
int rob(vector<int>& nums) {
if(nums.size()==0)return 0;
if(nums.size()==1)return nums[0];
int result1=robrange(nums,0,nums.size()-2);
int result2=robrange(nums,1,nums.size()-1);
return max(result1,result2);
}
int robrange(vector<int>& nums,int start,int end){
vector<int> dp(nums.size());
if(start==end)return nums[start];
dp[start]=nums[start];
dp[start+1]=max(nums[start],nums[start+1]);
for(int i=start+2;i<=end;i++){
dp[i]=max(nums[i]+dp[i-2],dp[i-1]);
}
return dp[end];
}
};
337.打家劫舍|||
题目链接:337. 打家劫舍 III - 力扣(LeetCode)
感觉这里的思路就是先把每个节点设置两个状态:
dp[0]:不偷当前节点获得的金钱
dp[1]:偷当前节点获得的金钱
我觉得这里的dp数组定义是这样没错,但是它其实是通过result1和result2来实现的
result2:不偷当前节点获得的金钱
就是 考虑左孩子(左孩子自己偷和不偷的最大值)+考虑右孩子(右孩子自己偷和不偷的最大值)
int result2=max(leftdp[0],leftdp[1])+max(rightdp[0],rightdp[1]);
reuslt1:偷当前节点获得的金钱
就是 当前节点的值+不偷左孩子的金钱+不偷右孩子的金钱
int result1=cur->val+leftdp[0]+rightdp[0];
然后从叶节点开始往上返回{不偷的钱,偷的钱}到根结点,然后根节点进行返回偷该结点和不偷该结点的最大值
//正确代码
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==NULL)return {0,0};
vector<int> leftdp=robtree(cur->left);
vector<int> rightdp=robtree(cur->right);
int result1=cur->val+leftdp[0]+rightdp[0];//偷
int result2=max(leftdp[0],leftdp[1])+max(rightdp[0],rightdp[1]);//不偷
return {result2,result1};//{不偷,偷}
}
};
做时候特别懵,为什么返回的时候一定是 {不偷当前节点的值,偷当前节点的值}一定是这个顺序
把这两者一换就错了,
//错误代码
int result1=cur->val+leftdp[0]+rightdp[0];//偷
int result2=max(leftdp[0],leftdp[1])+max(rightdp[0],rightdp[1]);//不偷
return {result1,result2};//{偷,不偷}
后来感觉是因为这样换的话dp数组的定义其实也换了
dp[0]是偷,dp[1]是不偷
那么在算偷的钱的时候也要换
偷该结点的时候要加上左右结点不偷的值,这里变成int result1=cur->val+leftdp[1]+rightdp[1]就行了
//改正
int result1=cur->val+leftdp[1]+rightdp[1];//偷
int result2=max(leftdp[0],leftdp[1])+max(rightdp[0],rightdp[1]);//不偷
return {result1,result2};