LeetCode 198. 打家劫舍 、213.打家劫舍II 、337. 打家劫舍 III (线性DP、环形DP、树形DP、记忆化搜索)

打家劫舍

  • 思路一
    d p [ i ] dp[i] 表示第 i i 个点必选的时候能够产生的最大值。
    那么 d p [ i ] = a [ i ] + m a x ( d p [ i 2 ] , d p [ i 3 ] ) dp[i] = a[i]+max(dp[i-2],dp[i-3]) ,当然前提是不越界。
    那么为什么 d p [ i 1 ] dp[i-1] 不依赖 d p [ i 4 ] dp[i-4] 以及更前面的值呢?如果 d p [ i ] dp[i] d p [ i 4 ] dp[i-4] 拓展来的,那么 d p [ i 2 ] dp[i-2] 选上就可以得到一个更大的值,换言之 d p [ i ] dp[i] 并不依赖于 d p [ i 4 ] dp[i-4] 和它前面的值。
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;
    }
};
  • 思路二
    以每一个点作为状态转移的阶段,
    d p [ i ] [ 0 ] d p [ i ] [ 1 ] dp[i][0],dp[i][1] 分别表示第i的点不选和必选两种情况下可以得到的最大值。
    那么:
    d p [ i ] [ 1 ] = d p [ i 1 ] [ 0 ] + a [ i ] dp[i][1] = dp[i-1][0]+a[i]
    d p [ i ] [ 0 ] = m a x ( d p [ i 1 ] [ 0 ] , d p [ i 1 ] [ 1 ] ) dp[i][0]=max(dp[i-1][0],dp[i-1][1])
    最终答案就是 m a x ( d p [ n ] [ 0 ] , d p [ n ] [ 1 ] ) max(dp[n][0],dp[n][1])
    当然,由于每个状态只依赖它前面的一个状态,空间复杂度可以优化到O(1)。
    空间复杂度: O ( 1 ) 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);
    }
};

猜你喜欢

转载自blog.csdn.net/qq_44846324/article/details/107455430
今日推荐