算法学习day48
- 1.力扣 198.打家劫舍
- 1.1 分析
- 1.2 代码
- 2.力扣213.打家劫舍II
- 2.1 分析
- 2.2 代码
- 3.力扣337.打家劫舍III
- 3.1 分析
- 3.2 代码
- 4.参考资料
1.力扣 198.打家劫舍
1.1 分析
1.确定dp数组以及下标的含义
dp[i] : 考虑下标i(包括i)以内的房屋,最多可以偷窃的金额数为dp[i]
2.确定递推公式
如果偷第i房间,dp[i] = dp[ i -2 ] + nums[i]。i - 1 房间不偷,下标i -2 (包括i - 2)以内的房间最多可以偷的金额为dp[i -2]加上第i房间偷的钱。
如果不偷第i房间,此时dp[i] = dp[ i -1]。
递推公式: dp[i] = max(dp[ i - 2]+ nums[i], dp[i - 1]);
3.dp数组如何初始化
由递推公式: dp[i] = max(dp[ i - 2]+ nums[i], dp[i - 1]); 递推公式的基础是dp[0]、dp[1]。
由dp的含义可知dp[0] = nums[0], dp[1] = max(nums[0] + nums[1])
初始化如下:
vector<int> dp(nums.size());
dp[0] = nums[0];
dp[1] = max(nums[0],nums[1]);
4.确定遍历顺序
dp[i] 是由dp[i-1]和dp[i-2]推导出来的,那么一定是从前到后遍历。
for(int i = 2; i < nums.size() ; i++){dp[i] = max(dp[i-2] + nums[i], dp[i -1]);
}
5.举例推导dp数组
1.2 代码
class Solution {
public:int rob(vector<int>& nums) { if(nums.size() == 0) return 0; // 当数组为空时,直接返回 0if(nums.size() == 1) return nums[0]; // 当数组只有一个元素时,直接返回该元素的值vector<int> dp(nums.size()); // 定义 dp 数组,长度为 nums 数组的长度dp[0] = nums[0]; // 初始化 dp[0] 为 nums[0]dp[1] = max(nums[0], nums[1]); // 初始化 dp[1],选择 nums[0] 和 nums[1] 中较大的一个for(int i = 2 ; i < nums.size(); i++){ // 遍历数组dp[i] = max(dp[i -2]+nums[i],dp[i-1]); // 状态转移方程}return dp[nums.size() - 1]; // 返回 dp 数组最后一个元素的值,即为所求}
};
2.力扣213.打家劫舍II
2.1 分析
1.考虑不包含首尾元素
2.考虑包含首元素,不包含尾元素
3.考虑包含尾元素,不包含首元素
2.2 代码
class Solution {
public:int rob(vector<int>& nums) {// 如果数组为空,直接返回0if(nums.size() == 0) return 0;// 如果数组长度为1,直接返回该数值if(nums.size() == 1) return nums[0];// 情况二:偷取[0, n-2]范围内的房屋int result1 = robRange(nums,0,nums.size() - 2);// 情况三:偷取[1, n-1]范围内的房屋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];// 初始化dp数组,dp[i]表示偷取[0,i]范围内的房屋所能获得的最大收益vector<int> dp(nums.size());dp[start] = nums[start];// 计算dp[1]dp[start+1] = max(nums[start],nums[start+1]);// 遍历数组,计算dp[i]for(int i = start+2; i<=end; i++){// 当偷取第i个房屋时,偷取范围只能是[0, i-2],因此dp[i] = dp[i-2] + nums[i]// 当不偷取第i个房屋时,偷取范围是[0, i-1],因此dp[i] = dp[i-1]dp[i] = max(dp[i-2] + nums[i], dp[i-1]);}// 返回偷取[start, end]范围内的房屋所能获得的最大收益return dp[end];}
};
3.力扣337.打家劫舍III
3.1 分析
1.确定递归函数的参数和返回值
一个节点,偷or不偷。返回值为长度为2的数组。
vector<int> robTree(TreeNode* cur){}
dp数组以及下标的含义:下标0表示不偷该节点获得的最大钱数,1表示偷该节点获得的最大钱数。
本题的dp数组是一个长度为2的数组。
2.确定终止条件
遍历过程中遇到空节点,返回。
if(cur == NULL) return vector<int>(0, 0);
3.确定遍历顺序
后序遍历。
// 下标0:不偷 下标1:偷
vector<int> left = robTree(cur->left);// 左
vector<int>right = robTree(cur->right);// 右
// 中
4.确定单层递归的逻辑
当前节点偷,由题意。左右孩子不能偷。val1= cur->val + left[0] + right[0];
如果不偷当前节点,左右孩子可偷。val2 = max(left[0],left[1]) + max(right[0],right[1]);
最后当前节点状态{val2,val1};{不偷当前节点得到的最大金钱,偷当前节点得到的最大金钱}
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};
5.举例推导dp数组
3.2 代码
class Solution {
public:int rob(TreeNode* root) {vector<int> result = robTree(root); // 获取最终结果return max(result[0], result[1]); // 返回最大值}// 辅助函数,返回一个长度为2的数组,表示在当前节点为根节点时偷或不偷的最大值vector<int> robTree(TreeNode* cur) {if (cur == NULL) return vector<int>{0, 0}; // 空节点返回0vector<int> left = robTree(cur->left); // 递归处理左子树vector<int> right = robTree(cur->right); // 递归处理右子树int val1 = cur->val + left[0] + right[0]; // 偷当前节点,左右子树都不能偷int val2 = max(left[0], left[1]) + max(right[0], right[1]); // 不偷当前节点,左右子树可偷可不偷return {val2, val1}; // 返回结果}
};
4.参考资料
[代码随想录]