今天晚上补动态规划中的打家劫舍的系列的问题,不算难,一口气拿下。
今日任务:
- 198.打家劫舍
- 213.打家劫舍II
- 337.打家劫舍III
题目一:198.打家劫舍
Leetcode题目:【198.打家劫舍】
当前房间的偷和不偷,会影响后面的选择,因此可以将其转化为一个动规的问题。
(1)确定dp数组含义:考虑下标i(包含i),他能偷的最大金额为dp[i],最后就是dp[nums.size()-1]
(2)我们的递推公式:
偷i:dp[i] = dp[i-2] + nums[i]
不偷i:dp[i] = dp[i-1]
dp[i] = max(dp[i-1], dp[i-2] + nums[i]);
(3)初始化:
dp[0] = nums[0];
dp[1] = max(nums[0], nums[1]);
(4)遍历顺序:
从前往后依次遍历就可以了。
(5)打印dp数组。
此题的注意点为:前面需要判断如果是0或者1的情况。
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() + 1);
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
Leetcode题目:【213.打家劫舍II】
这个与上面那个题就是数组成环了。如果连成环的话,应该如何思考:
这样可以分为三种情况:
(1)不选首元素也不选尾元素;
(2)考虑首元素,不考虑尾元素;
(3)考虑尾元素,不考虑首元素。
这个就是第二种情况是包含情况一的,情况三也是包含情况一的。因此只需要比较情况二和情况三的最大值,情况二和情况三都是一个线性表,因此就是利用两次相同的函数,然后比较谁大,最后就返回谁的值。
再回到上面的非环的数组,按照递归五部曲的分析如下:
(1)确定dp数组的含义,dp[i]表示考虑到下标i,可以偷到的最大金额数为dp[i];
(2)递推公式:
偷:dp[i] = dp[i-2] + nums[i];
不偷:dp[i] = dp[i-1]
dp[i] = max(dp[i-1], dp[i-2] + nums[i])
(3)初始化:
dp[0] = nums[0];
dp[1] = max(nums[0],nums[1]);
(4)遍历顺序:从小到大即可。
(5)打印dp数组;
注意点:函数的构造
class Solution {
public:
int maxValue(vector<int> nums, int begin, int end){
vector<int> dp(nums.size());
dp[begin] = nums[begin];
dp[begin+1] = max(nums[begin], nums[begin+1]);
for(int i = begin + 2; i <= end; i++){
dp[i] = max(dp[i-1], dp[i-2] + nums[i]);
}
return dp[end];
}
int rob(vector<int>& nums) {
if(nums.size() == 0) return 0;
if(nums.size() == 1) return nums[0];
int tradition2 = maxValue(nums, 0, nums.size()-2);
int tradition3 = maxValue(nums, 1, nums.size()-1);
return max(tradition2, tradition3);
}
};
题目三:337.打家劫舍III(太难了,就简单欣赏下吧)
Leetcode题目:【337.打家劫舍III】
此题是树形dp的一个入门题目,递归三部曲 + 动规五部曲。
一维的dp数组,dp[0]表示不偷当前节点,获得的最大金钱;dp[1]表示偷,获得的最大金钱。
二叉树采用后序遍历(左、右、中)。
可以理解,但是不好写。
仅简单欣赏下代码:
class Solution {
public:
int rob(TreeNode* root) {
vector<int> result = robTree(root);
return max(result[0], result[1]);
}
// 长度为2的数组,0:不偷,1:偷
vector<int> robTree(TreeNode* cur) {
if (cur == NULL) return vector<int>{0, 0};
vector<int> left = robTree(cur->left);
vector<int> right = robTree(cur->right);
// 偷cur,那么就不能偷左右节点。
int val1 = cur->val + left[0] + right[0];
// 不偷cur,那么可以偷也可以不偷左右节点,则取较大的情况
int val2 = max(left[0], left[1]) + max(right[0], right[1]);
return {val2, val1};
}
};