代码随想录算法训练营(动态规划9)|198.打家劫舍 & 213.打家劫舍II & 337.打家劫舍III
今天就是打家劫舍的一天,微笑
198.打家劫舍
动规五部曲分析如下:
-
确定dp数组(dp table)以及下标的含义
dp[i]:考虑下标i(包括i)以内的房屋,最多可以偷窃的金额为dp[i]。 -
确定递推公式
决定dp[i]的因素就是第i房间偷还是不偷。
如果偷第i房间,那么dp[i] = dp[i - 2] + nums[i] ,即:第i-1房一定是不考虑的,找出 下标i-2(包括i-2)以内的房屋,最多可以偷窃的金额为dp[i-2] 加上第i房间偷到的钱。
如果不偷第i房间,那么dp[i] = dp[i - 1],即考 虑i-1房,(注意这里是考虑,并不是一定要偷i-1房,这是很多同学容易混淆的点)
然后dp[i]取最大值,即dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
- dp数组如何初始化
从递推公式dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);可以看出,递推公式的基础就是dp[0] 和 dp[1]
从dp[i]的定义上来讲,dp[0] 一定是 nums[0],dp[1]就是nums[0]和nums[1]的最大值即:dp[1] = max(nums[0], nums[1]);
class Solution {
public:
int rob(vector<int>& nums) {
//1. dp数组的含义 是dp[i] 0到i,的最优选项,不一定包含当前节点
vector<int> dp(nums.size(), 0);
if(nums.size() == 0) return 0;
if(nums.size() == 1) return nums[0];
//2. 递推公式
//当前节点的结果有两种情况一中是投当前节点,一种是不偷当前节点
//偷当前节点就是,前一个节点的最优解:dp[i-1]
//不偷当前节点就是前一个节点不能偷,所以是dp[i-2] + nums[i];
//所以递推公式就是 dp[i] = max(dp[i-1], dp[i-2] + nums[i];
//3. 初始化
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
将环形数组,根据题目描述可以,分解为两种数组
// 注意注释中的情况二情况三,以及把198.打家劫舍的代码抽离出来了
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);
}
// 198.打家劫舍的逻辑
int robRange(vector<int>& nums, int start, int end) {
if (end == start) return nums[start];
vector<int> dp(nums.size());
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 - 2] + nums[i], dp[i - 1]);
}
return dp[end];
}
};
337.打家劫舍III
leetcode题目链接
视频讲解
文章讲解
动态规划结合树形结构,比较难
class Solution {
public:
//int dp[2] = {0}; dp[0] 表示不偷当前节点,dp[1] 表示偷当前节点
vector<int> robTree(TreeNode * node) {
if(node == nullptr) return {0,0};
vector<int> left(2,0);
vector<int> right(2,0);
left = robTree(node->left); //左
right = robTree(node->right);//右
// 中,后续遍历,已知左右的值推出当前节点的值
//不偷当前节点就是要偷 左节点和有节点, 将左节点偷和不偷的最大值和右节点头或者不偷的最大值加一起
int tmp1 = max(left[0], left[1]) + max(right[0], right[1]);
//偷当前节点的话, 就是将当前节点的值加上,左节点和有节点都偷的和
int tmp2 = node->val + left[0] + right[0];
return {tmp1, tmp2};
}
int rob(TreeNode* root) {
vector<int> vec(2,0);
vec = robTree(root);
//返回根节点偷或者不偷的最大值
return max(vec[0], vec[1]);
}
};
原文地址:https://blog.csdn.net/he979731102/article/details/136399855
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!