Family Robbery Series: 198. Family Robbery 213. Family Robbery 2 337. Family Robbery 3

198. House Robbery 1

The linear dp can finally use my own ideas, f[i] represents the largest gold coin that can be obtained by stealing the i-th house, so it must not steal the i-1th house, that is, transfer from f[i-2] Come here, but I can also not steal i-2, so I can transfer from f[i-3], but I have to steal i-3, because if there are 3 gaps in the middle, I must steal another one in the middle. (Assuming that the data are all positive), so naturally think of the transfer equation f[i]=max(f[i-2],f[i-3])+nums[i-3]; the final answer must be the last two f In, because the previous one will definitely steal the last one.

class Solution {
public:
    int rob(vector<int>& nums) {
        if (nums.size()==0) return 0;
        int n=nums.size();
        vector<int> f(n+10,0);
        //int ans=0;
        for (int i=0+3;i<n+3;i++)
        {
            f[i]=max(f[i-2],f[i-3])+nums[i-3];
            //ans=max(ans,f[i]);
        }
        return max(f[n+3-1],f[n+3-2]);

    }
};

213. The House Robbery 2

The ring arrangement means that only one of the first house and the last house can be selected for theft . Therefore, the ring arrangement room problem can be reduced to two single row arrangement room sub-problems:

In the case of not stealing the first house ( ie nums[1:] ), the maximum amount is p_1.
Without stealing the last house ( ie nums[:n-1] ), the maximum amount is p_2
Comprehensive theft maximum Amount: It is the larger value of the above two cases, namely max(p1,p2).
The next task is to solve the problem of arranging rooms in a row (that is, 198. House Robbery).

class Solution {
public:
    int rob(vector<int>& nums) {
        if (nums.size()==0) return 0;
        int n=nums.size();
        if (n==1) return nums[0];
        if (n==2) return max(nums[0],nums[1]);
        if (n==3) return max(max(nums[0],nums[1]),nums[2]);
        vector<int> f(n+10,0);
        int ans=0;
        //偷第一家,不偷最后一家即[0..n-2]
        for (int i=0+3;i<n-1+3;i++)
        f[i]=max(f[i-2],f[i-3])+nums[i-3];
        ans=max(f[n-2+3],f[n-3+3]);

        f.clear();
        f.resize(n+10);
        //偷最后一家,不偷第一家即[1..n-1]
        for (int i=1+3;i<n+3;i++)
        f[i]=max(f[i-2],f[i-3])+nums[i-3];
        ans=max(max(f[n-1+3],f[n-2+3]),ans);

        return ans;

    }
};

There is a problem-solving idea that is the same as mine, and it feels better than me. .

https://leetcode-cn.com/problems/house-robber-ii/solution/213-c-shuang-100-da-jia-jie-she-by-smart_shelly/

 

337. The House Robber 3

Tree type dp? At first I thought that the thief could not turn back, so I directly put this tree on the array f[i]=max(f[i/2/2],f[i/2/2/2])+v [i];It’s ok. After writing, I found that this thief was really greedy, and if he had to go back, he had to traverse the whole tree. I used dfs but got stuck at the 123rd test point, so I wanted to use it. Memoization optimization is coming, but the state (TreeNode* tn, int flag) is not easy to express. I took a peek at the problem solution. Generally, they directly hashmap a state TreeNode* tn, so they don’t need to record the state flag. They are another way of thinking, namely

Failure to rob the root node affects the profit of looting a tree:
    looting the root node cannot rob the left and right child nodes, but can rob the four subtrees of the left and right child nodes.
    If the root node is not robbed, the left child node and the right child node can be robbed. The income is the sum of the income of robbing the left and right subtrees.

In this case, it is sufficient to record a state TreeNode* tn, but my idea is to recursively left and right subtrees by marking whether the current node is stolen, and does not involve grandchildren.

/**
 * 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:
    int dg(TreeNode* root,int flag)
    {
        if (root==NULL) return 0; 
        if (flag==1)
        {
            int ans=dg(root->left,0)+dg(root->right,0);
            return ans;
        }
        else
        {
            int ans1=dg(root->left,0)+dg(root->right,0);
            int ans2=dg(root->left,1)+dg(root->right,1)+root->val;
            return max(ans1,ans2);
        }

    }
    int rob(TreeNode* root) {
        if (root==NULL) return 0;
        return max(dg(root,1),dg(root,0));
    }
};

But this kind of code stuck at the 123rd test point.

So I continued to look at the problem solution for optimization, and saw a feasible optimization, that is, I also found that there are many repeated recursion, for example, int ans1=dg(root->left,0)+dg(root->right, 0); In this sentence, almost every node will need to recalculate without stealing the current node. So the improvement direction is to calculate the stealing and non-stealing in one traversal, then save it, and return to the current node to call at will.

class Solution {
public:
    vector<int> dg(TreeNode* tn)
    {
        vector<int> ans(2,0);
        if (tn==NULL) return ans; 

        vector<int> left=dg(tn->left);
        vector<int> right=dg(tn->right);
        
        ans[0]=max(left[0],left[1])+max(right[0],right[1]);
        ans[1]=left[0]+right[0]+tn->val;
        
        return ans;
    }

    int rob(TreeNode* root) {
        vector<int> ans=dg(root);
        return max(ans[0],ans[1]);
    }
};

Here we must pay attention to the risk of return type vector<int>

One is that if the content of the vector is not a basic data type, you need to write the copy constructor yourself, otherwise only a shallow copy will be performed.

The second is because there will be a copy process, if the vector data is very large, it will lead to a decrease in efficiency. //So the best way is to call by passing the vector reference as a parameter.

The third is that this method is successful because a temporary object will be generated when the function returns. This temporary object is used after the return, and the original local variables have been released. But it should be noted that the optimization mechanism of g++ compilation may cause this temporary object to not be generated, and then an error. I found this problem on linux, see https://www.veaxen.com/%E5%A4%8D%E6%9D%82%E7%9A%84c%EF%BC%8C%E5%BD %93%E5%87%BD%E6%95%B0%E8%BF%94%E5%9B%9E%E5%AF%B9%E8%B1%A1%E5%88%B0%E5%BA%95 %E5%8F%91%E7%94%9F%E4%BA%86%E4%BB%80%E4%B9%88%EF%BC%9F.htmlThis article only knows that the compilation environment will also make the return result different.

Reposted from the comment discussion on this blog: https://blog.csdn.net/neverever01/article/details/80744148

Therefore, the vector is not easy to use, we still have the old way to pass the array pointer, see

https://blog.csdn.net/weixin_41232202/article/details/90368296

class Solution {
public:
    int * dg(TreeNode* tn)
    {
        int * ans=new int[2]{0,0};
        if (tn==NULL) return ans; 

        int * left=dg(tn->left);
        int * right=dg(tn->right);
        
        ans[0]=max(left[0],left[1])+max(right[0],right[1]);
        ans[1]=left[0]+right[0]+tn->val;
        
        return ans;
    }

    int rob(TreeNode* root) {
        int * ans=dg(root);
        return max(ans[0],ans[1]);
    }
};

Reduced memory consumption, but the execution time is still only 50%. Let's do this first, find an opportunity to improve it, and see how to write tree dp .

Well, take a look at the official solution, this is the tree dp

Tree DP is different from regular DP in that it does not "fill in the form" in iteration, but "fill in form" in recursion.
The two-dimensional matrix of this question is actually:
dp[root][0]: rob the subtree with root as the root node, and the maximum benefit of not robbing the root node
dp[root][1]: rob root as the root node subtree, and robbery root node of the biggest gains
in the analysis, pay attention to the relationship between root and child nodes conflicting.

See specifically: https://leetcode-cn.com/problems/house-robber-iii/solution/si-chong-xie-fa-di-gui-ji-yi-hua-di-gui-shu-xing-d /

There is another way to return tuples like python, c++ structure has {} this way:

struct SubtreeStatus {
    int selected;
    int notSelected;
};

class Solution {
public:
    SubtreeStatus dfs(TreeNode* o) {
        if (!o) {
            return {0, 0};
        }
        auto l = dfs(o->left);
        auto r = dfs(o->right);
        int selected = o->val + l.notSelected + r.notSelected;
        int notSelected = max(l.selected, l.notSelected) + max(r.selected, r.notSelected);
        return {selected, notSelected};
    }

    int rob(TreeNode* o) {
        auto rootStatus = dfs(o);
        return max(rootStatus.selected, rootStatus.notSelected);
    }
};

The execution time reached 90%. . . This is probably the technique.

At this point, the problem of house robbery has come to an end.

to be continued......

Guess you like

Origin blog.csdn.net/hbhhhxs/article/details/107810462