198.打家劫舍
题目链接/文章讲解/视频讲解:代码随想录
1.代码展示
//198.打家劫舍
int rob(vector& nums) {
//step1 构建dp数组
//本题dp[i]的含义是偷到第i + 1家的最多金币数
if (nums.size() == 1) {
return nums[0];
}
vector dp(nums.size(), 0);
//step2 状态转移方程
//dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])
//dp[i]受到前一个房间是否被偷的影响,选择盗取金额最大的情况
//step3 初始化dp数组,dp[i]的推导依赖两个初始值
dp[0] = nums[0];
dp[1] = max(nums[0], nums[1]);
//step4 确定遍历顺序
for (int i = 2; i < nums.size(); i++) {
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i]);
//step5 打印数组
}
return dp[nums.size() - 1];
}
2.本题小节
思考:首先要明确本题dp[i]的含义,指的是偷到i + 1家的最多金币数,根据题目的定义,如果选择偷第i + 1家,那么dp[i] = dp[i - 2] + nums[i],如果不偷第i + 1家,那么dp[i] = dp[i - 1],因此,是否偷第i + 1家取决于这两种情况哪一种更大,则状态转移公式为dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]),根据状态转移公式给dp[0],dp[1]赋初值,并对nums进行遍历,最后返会dp数组的最后一个结果即可。
基本思路:见思考与代码,思考已经写得比较清楚了。
213.打家劫舍II
题目链接/文章讲解/视频讲解:代码随想录
1.代码展示
int rob(vector& nums) {
//step1 构建dp数组
//本题dp[i]的含义是偷到第i + 1家的最多金币数
if (nums.size() == 1) {
return nums[0];
}
vector dp(nums.size(), 0);
//step2 状态转移方程
//dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])
//dp[i]受到前一个房间是否被偷的影响,选择盗取金额最大的情况
//step3 初始化dp数组,dp[i]的推导依赖两个初始值
dp[0] = nums[0];
dp[1] = max(nums[0], nums[1]);
//step4 确定遍历顺序
for (int i = 2; i < nums.size(); i++) {
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i]);
//step5 打印数组
}
return dp[nums.size() - 1];
}
//213 打家劫舍Ⅱ
int rob2(vector& nums) {
if (nums.size() == 1) {
return nums[0];
}
vector vnfront(nums.begin(), nums.end() - 1);
vector vnTail(nums.begin() + 1, nums.end());
return max(rob(vnfront), rob(vnTail));
}
2.本题小节
思考:要明确首尾的房子不能同时被窃取,因此只需要考虑两种情况,不包含最后一个房子和不包含第一个房子这两种情况,因此可以将这两种情况的数组传入到上一题的代码中,分别得到这两种情况的能够盗取金额的最大值,最后再比较返回最大值即可。
337.打家劫舍III
题目链接/文章讲解/视频讲解:代码随想录
1.代码展示
//337 打家劫舍Ⅲ
struct TreeNode {
TreeNode* left;
TreeNode* right;
int val;
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){}
};
vector robTravelsal(TreeNode* node, vector& dp) {
//step1 确定参数
//step2 确定终止条件
if (node == nullptr) {
return { 0, 0 };
}
//step3 确定单层迭代逻辑
vector vnLeft = robTravelsal(node->left, dp);
vector vnRight = robTravelsal(node->right, dp);
dp[0] = max(vnLeft[0], vnLeft[1]) + max(vnRight[0], vnRight[1]);
dp[1] = node->val + vnLeft[0] + vnRight[0];
return dp;
}
int rob(TreeNode* root) {
//step1 构建dp数组
//dp数组的含义是在当前节点下不偷当前节点,金额为dp[0],偷当前节点,金额为dp[1]
vector dp(2, 0);
robTravelsal(root, dp);
return max(dp[0], dp[1]);
}
2.本题小节
思考:本题是dp二叉树的入门题目,本题的遍历顺序为后序遍历,返回值为dp数组,dp数组的大小为2,dp[0]、dp[1]的含义分别时当前节点的房子不被偷和被偷的最大金额,如果不被偷,则dp[0] = max(left[0], left[1]) + max(right[0], right[1]),也就是左右子节点的最大值相加,如果被偷,则dp[1] = node->val + left[0] + right[0],即当前值加上左右节点没被偷的值。
基本步骤:根据二叉树的递归三部曲来做,第一步,确定参数,其中dp数组的含义要明确;第二步,终止条件是到子节点,返回{0,0};第三步,左右依次进行递归,然后根据左右遍历结果来更新dp数组。在rob函数中通过获取的dp数组,取其中的最大值即为所求。