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};
}
};