198.打家劫舍
题目链接 文章讲解 视频讲解
- dp[j]: 表示投到第j家最多能偷dp[j]的钱
- 递推公式: dp[j] = max(dp[j-2] + nums[j], dp[j-1])
- 初始化:dp[0] = nums[0], dp[1] = max(dp[0], dp[1])
- 遍历顺序:从小到大
- 打印dp数组
class Solution {
public:int rob(vector<int>& nums) {// dp[j]: 表示偷第j家最多能偷多少int n = nums.size();vector<int> dp(n);if(n == 0) return 0;if(n == 1) return nums[0];// 递推公式: dp[j] = dp[j - 2] + nums[j]// 初始化:dp[0] = nums[0];dp[1] = max(nums[0], nums[1]);for(int j = 2; j < n; ++j) {dp[j] = max(dp[j - 2] + nums[j], dp[j-1]);}for(int val : dp) cout << val << " ";return dp[n - 1];}
};
213.打家劫舍II
题目链接 文章讲解 视频讲解
思路: 首元素和尾元素只能有一个,所以分两种情况,加入首元素和加入尾元素分别求,然后取最大的
class Solution {
public:int rob(vector<int>& nums) {int n = nums.size();vector<int> dp(n);if(n == 0) return 0;if(n == 1) return nums[0];if(n == 2) return max(nums[0], nums[1]);return max(robRange(nums, 0, n -1), robRange(nums, 1, n));}int robRange(vector<int>& nums, int start, int end) {vector<int> dp(end);dp[start] = nums[start];dp[start + 1] = max(nums[start], nums[start + 1]);for(int j = start + 2; j < end; ++j) {dp[j] = max(dp[j - 2] + nums[j], dp[j-1]);}return dp[end-1];}
};
打家劫舍III
题目链接 文章讲解 视频讲解
动规五部曲:
- dp[0] dp[1]分别表示不偷当前节点得到的最大值和偷当前节点的最大值
- 递推公式:
- 偷当前节点: dp[0] = cur->val + left_dp[0] + right_dp[0],如果偷了当前节点,那么左右孩子就不可以偷了
- 不偷当前节点:dp[1] = max(left_dp[0], left[1]) + max(right_dp[0], right_dp[1]) 分别计算左右孩子偷与不偷的最大值相加即可
- 初始话:初始化为任意值都可以因为dp值不以来当前dp数组,而只依赖于左右孩子的dp数组
- 遍历顺序:由递推公式可知,要计算当前dp值需要先获取左右孩子的dp值,所以应用后序遍历
class Solution {
public:int rob(TreeNode* root) {// dp[0]表示不偷当前节点的最大值,dp[1]表示偷当前节点的最大值vector<int> dp = traversal(root);return max(dp[0], dp[1]);}vector<int> traversal(TreeNode* root) {if(root == nullptr) return {0, 0};vector<int> left_dp = traversal(root->left);vector<int> right_dp = traversal(root->right);int val1 = root->val + left_dp[0] + right_dp[0];int val2 = max(left_dp[0], left_dp[1]) + max(right_dp[0], right_dp[1]);return {val2, val1};}
};