198.打家劫舍
解题思路
1. dp[i] 考虑下标i(包含),能偷的最大金币是dp[i]
2.递推公式
偷i:dp[i-2] + nums[i] (i-2之前是考虑范围)
不偷i:dp[i-1] (i-1之前是考虑范围),不一定非要偷
dp[i] = max(上面两个)
3.初始化
dp[0] = nums[0];
dp[1] = max(nums[0],nums[1]) 不一定非得偷1,偷这两个房间其中一个的最大值(根据定义)
非0初始为0
4.从小到大去遍历
class Solution {
public:
int rob(vector<int>& nums) {
if(nums.size()==1) return nums[0];
if(nums.size()==2) return max(nums[0],nums[1]);
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];
}
};
213.打家劫舍II
解题思路
解决环的问题:
其实我们可以直接分为三种情况
情况1:不考虑首尾
情况2:考虑头不考虑尾
情况3:考虑尾不考虑头
情况2和情况3已经包含情况1了(仅仅是遍历的范围,并不一定要取)
class Solution {
private:
int robRange(vector<int>& nums , int start , int end)
{
if(end==start) return nums[end];
vector<int> dp(nums.size(),0);
dp[start] = nums[start];
dp[start+1] = max(nums[start],nums[start+1]);
for(int i=start+2 ; i<=end ; i++)
{
dp[i] = max( dp[i-1] , dp[i-2]+nums[i]);
}
return dp[end];
}
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);
}
};
337.打家劫舍III
解题思路
树形DP
1.dp含义 dp[0]表示不偷的最大金钱,dp[1]表示偷的最大金钱,那么每个节点就会有一个vector<int> nodeDp{不偷,偷}
2. 返回当前节点偷或者不偷的最大金币数量的递归函数
vector<int> robtree(TreeNode* cur)
{
if(cur==nullptr) return vector<int>{0,0}; //空节点偷和不偷,价值都是0
//后序遍历,先处理左右孩子,方便后面判断
vector<int> leftdp = robtree(cur->left);
vector<int> rightdp = robtree(cur->right);
int val1 = cur->val + leftdp[0] + rightdp[0]; //偷当前节点,和左右孩子不偷的情况
int val2 = max(leftdp[0],leftdp[1]) + max(right[0]+rightdp[1]); //左右孩子偷不偷要取一个最大的
return {val2,val1}; //表示偷和不偷当前节点
}
class Solution {
public:
vector<int> robtree(TreeNode* cur)
{
if(cur==nullptr) return vector<int>{0,0}; //空节点偷和不偷,价值都是0
//后序遍历,先处理左右孩子,方便后面判断
vector<int> leftdp = robtree(cur->left);
vector<int> rightdp = robtree(cur->right);
int val1 = cur->val + leftdp[0] + rightdp[0]; //偷当前节点,和左右孩子不偷的情况
int val2 = max(leftdp[0],leftdp[1]) + max(rightdp[0],rightdp[1]); //左右孩子偷不偷要取一个最大的
return {val2,val1}; //表示偷和不偷当前节点
}
int rob(TreeNode* root) {
vector<int> result = robtree(root);
return max(result[0],result[1]);
}
};
- 时间复杂度:O(n),每个节点只遍历了一次
- 空间复杂度:O(log n),算上递推系统栈的空间
收获
有点难