- 思路一
用 表示第 个点必选的时候能够产生的最大值。
那么 ,当然前提是不越界。
那么为什么 不依赖 以及更前面的值呢?如果 是 拓展来的,那么 选上就可以得到一个更大的值,换言之 并不依赖于 和它前面的值。
class Solution {
public:
int rob(vector<int>& a) {
int n = a.size();
vector<int> dp(n+1,0);
int ans = 0;
for(int i=1;i<=n;i++){
dp[i] = a[i-1];
if(i>=3){
dp[i] += max(dp[i-2],dp[i-3]);
}else if(i>=2){
dp[i] += dp[i-2];
}
ans = max(ans,dp[i]);
}
return ans;
}
};
- 思路二
以每一个点作为状态转移的阶段,
用 分别表示第i的点不选和必选两种情况下可以得到的最大值。
那么:
最终答案就是
当然,由于每个状态只依赖它前面的一个状态,空间复杂度可以优化到O(1)。
空间复杂度:
class Solution {
public:
int rob(vector<int>& a) {
int unselected = 0;
int selected = 0;
for(int num : a) {
//当前状态不选的情况,可能前一个选,也可能不选
int unselected_temp = max(unselected, selected);
//当前状态选的情况,必须是前一个不选
selected = unselected + num;
unselected = unselected_temp;
}
return max(unselected, selected);
}
};
打家劫舍II
有上一道题目的铺垫,这一道拓展的题目好想多了
分三种情况考虑;
这里的状态依赖构成了一个环,于是我们把这个环从某个地方切割开,然后就没有了后效性(最后一个点没必要关心第一个点的情况了)—— 我们只要分情况讨论所有情况即可。
① 选第一个点
② 选最后一个点。
③ 第一个点和最后一个点都不选。
class Solution {
public:
int rob(vector<int>& a) {
int n = a.size();
if(n==0){
return 0;
}
return max(max(a[0]+helper(a,2,n-2),a[n-1]+helper(a,1,n-3)),helper(a,1,n-2));
}
int helper(const vector<int>& a,int l,int r) {
if(l>r) return 0;
int unselected = 0;
int selected = 0;
for(int i = l;i<=r;i++) {
//当前状态不选的情况,可能前一个选,也可能不选
int unselected_temp = max(unselected, selected);
//当前状态选的情况,必须是前一个不选
selected = unselected + a[i];
unselected = unselected_temp;
}
return max(unselected, selected);
}
};
打家劫舍 III
思路就是第一道题的思路二,用map<TreeNode*,int> mp[2];
记录某个点选和不选的产生的最大值。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
map<TreeNode*,int> mp[2];
int rob(TreeNode* root) {
return max(dfs(root,1),dfs(root,0));
}
int dfs(TreeNode* root,bool selectable){
if(root==nullptr) return 0;
if(mp[selectable].count(root)){
return mp[selectable][root];
}
int ans ;
if(selectable){
ans = max(root->val+dfs(root->left,0)+dfs(root->right,0),dfs(root->left,1)+dfs(root->right,1));
}else{
ans = dfs(root->left,1)+dfs(root->right,1);
}
return mp[selectable][root] = ans;
}
};
思路就是第一道题的思路一,用map<TreeNode*,int> mp
记录以某个点为子树产生的最大值,其中,这个点它自己必选。
但这样一个点就依赖了它的孙子节点。
class Solution {
public:
unordered_map<TreeNode*,int> mp;
int rob(TreeNode* root) {
if(!root) return 0;
if(mp.count(root)) return mp[root];
int ans = rob(root->left)+rob(root->right);
int temp = root->val;
if(root->left){
temp += rob(root->left->left)+rob(root->left->right);
}
if(root->right){
temp += rob(root->right->left)+rob(root->right->right);
}
ans = max(ans,temp);
return mp[root] = ans;
}
};
对第一种记忆化递归的空间压缩,因为每个点只依赖它的两个子节点。
求出每个节点自己选和不选两种情况产生的能够产生的最大值。
这像极了打家劫舍|的思路二。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
typedef pair<int,int> P;
int rob(TreeNode* root) {
P p = helper(root);
return max(p.first,p.second);
}
P helper(TreeNode* root){
if(!root) return make_pair(0,0);
P pl = helper(root->left);
P pr = helper(root->right);
int m0 = max(pl.first,pl.second)+max(pr.first,pr.second);
int m1 = pl.first+pr.first+root->val;
return make_pair(m0,m1);
}
};