198.打家劫舍
class Solution {
public:
int rob(vector<int>& nums) {
// 1. dp[j]表示j个房屋最多偷多少
// 2. dp[j] = max(dp[j-1], nums[j - 1] + dp[j-2])
// 3. 初始化dp[0]=0,dp[1] = nums[0], 其余为0
// 4. 遍历方向:从左到右
vector<int> dp(nums.size() + 1, 0);
dp[1] = nums[0];
for (int i = 2; i < nums.size() + 1; ++i) {
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i - 1]);
}
return dp[nums.size()];
}
};
213.打家劫舍II
这一题实际上就是多次调用上面的代码。考虑使用头节点不使用尾节点的情况和使用尾节点不使用头节点的情况。
class Solution {
public:
int robRange(const vector<int>& nums, int start, int end) {
// 1. dp[i]表示到第i个房屋最多偷多少
// 2. dp[i] = max(dp[i-1], nums[i] + dp[i-2])
// 3. 初始化dp[0]=nums[0],dp[1] = max(nums[0], nums[1]), 其余为0
// 4. 遍历方向:从左到右
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], nums[i] + dp[i-2]);
}
return dp[end];
}
int rob(vector<int>& nums) {
if (nums.size() == 1) return nums[0];
if (nums.size() == 2) return max(nums[0], nums[1]);
int result1 = robRange(nums, 0, nums.size() - 2);
int result2 = robRange(nums, 1, nums.size() - 1);
return max(result1, result2);
}
};
337.打家劫舍 III
这是第一次遇见树形dp。思路是递归三部曲的思路,选择后序遍历。
- 确定参数及返回值:参数是节点,返回值是dp数组,dp数组的含义是包含当前节点的最大值和不包含当前节点的最大值。
- 确定终止条件:遇到null返回0
- 单层处理逻辑:不包含当前节点的最大值是通过求左节点最大值与右节点最大值之和,包含当前节点的最大值是通过求当前节点+左节点不包含+右节点不包含。
class Solution {
public:
vector<int> robdp(TreeNode* node) {
if (node == nullptr) return {0, 0};
vector<int> leftdp = robdp(node->left);
vector<int> rightdp = robdp(node->right);
int val0 = max(leftdp[0], leftdp[1]) + max(rightdp[0], rightdp[1]);
int val1 = node->val + leftdp[0] + rightdp[0];
return {val0, val1};
}
int rob(TreeNode* root) {
vector<int> dp = robdp(root);
return max(dp[0], dp[1]);
}
};