198. 打家劫舍
dp方程:dp[i] = max(dp[i-2] + nums[i] , dp[i-1])
- dp[i]表示偷了 i 家时,获得的最大收益
- nums[i]表示当前准备偷的房屋有多少钱
理解下来就是:偷了 i 家后,我所获得的最大收益要么等于偷完上一家的收益(不偷当前这家),要么等于偷完i-2家和当前房屋的金额之和(偷当前这家)
int rob(vector<int>& nums) {
if(nums.size() == 0) return 0;//没有房屋
if(nums.size() == 1) return nums[0];//只有一间房屋
int len = nums.size();
vector<int> dp(len);
dp[0] = nums[0];//偷了第0家
for(int i=1; i<len; i++){
if(i == 1){
dp[i] = max(nums[i] , dp[i-1]);//偷第1家
}else{
dp[i] = max(dp[i-2] + nums[i] , dp[i-1]);
}
}
return dp[len-1];
}
优化一下,可以去掉dp数组。由于dp数组只是记录了当前获取的最大收益,找个变量记录一下即可。当然上述代码中的dp[i-2]也得用变量记录
int rob(vector<int>& nums) {
if(nums.size() == 0) return 0;
if(nums.size() == 1) return nums[0];
if(nums.size() == 2) return nums[0] > nums[1] ? nums[0] : nums[1];//只有两个房屋时
int len = nums.size();
int cur_profit;
int pre_2 = nums[0];//dp[i-2]
int pre_1 = nums[0] > nums[1] ? nums[0] : nums[1];//dp[i-1]
for(int i=2; i<len; i++){
cur_profit = max(pre_2 + nums[i] , pre_1);
pre_2 = pre_1;
pre_1 = cur_profit;
}
return cur_profit;
}
213. 打家劫舍 II
分成两个区间 [0, n-2] [1, n-1] 偷即可
class Solution {
public:
int rob(vector<int>& nums, int l, int r) {
int n = r - l + 1;
if(n == 1) return nums[l];
vector<int> dp(n, 0);
dp[0] = nums[l];
dp[1] = max(nums[l], nums[1 + l]);
for(int i = 2; i < n; i++){
dp[i] += max(dp[i - 2] + nums[l + i], dp[i - 1]);
}
return dp[n - 1];
}
int rob(vector<int>& nums) {
int n = nums.size();
if(n == 1) return nums[0];
return max(rob(nums, 0, n - 2), rob(nums, 1, n - 1));
}
};
337. 打家劫舍 III
树的问题,很多时候采用 后序遍历:先处理左右孩子结点,再处理当前结点,整个过程看起来就像一层一层向上汇报信息
第 1 步:状态定义
dp[node][j] :这里 node 表示一个结点,以 node 为根结点的树,并且规定了 node 是否偷取能够获得的最大价值
- j = 0 表示 node 结点不偷取;
- j = 1 表示 node 结点偷取
第 2 步: 推导状态转移方程
根据当前结点偷或者不偷,就决定了需要从哪些子结点里的对应的状态转移过来。
- 如果当前结点不偷,左右子结点偷或者不偷都行,选最大者;
- 如果当前结点偷,左右子结点均不能偷。
第 3 步: 初始化
一个结点都没有,空节点,返回 0,对应后序遍历时候的递归终止条件;
第 4 步: 输出
在根结点的时候,返回两个状态的较大者。
class Solution {
public:
unordered_map<TreeNode*, vector<int>> dp;
void dfs(TreeNode* root){
if(root == nullptr) return ;
dfs(root->left);
dfs(root->right);
dp[root] = { 0, 0 };
// if(root->left == nullptr && root->right == nullptr) {
// // 叶节点
// dp[root][0] = 0;
// dp[root][1] = root->val;
// return;
// }
dp[root][0] = max(dp[root->left][0], dp[root->left][1]) + max(dp[root->right][0], dp[root->right][1]);
dp[root][1] = root->val + dp[root->left][0] + dp[root->right][0];
}
int rob(TreeNode* root) {
// 当前节点偷/不偷,能获得的最大收益
dp[nullptr] = {0, 0};
dfs(root);
return max(dp[root][0], dp[root][1]);
}
};