Summary of a series of questions about house raiding

Keywords: Algorithm

1. House raiding

thinking

Conventional dynamic programming problems

① dp[i] means the maximum amount of money to steal the ith house

② Recursion dp[i]=max(dp[i-1], dp[i-2]+nums[i]) is actually to find the maximum amount of the ith house, that is, either steal the house or not. If you steal, the previous one cannot be stolen, that is, find the maximum amount of dp[i-2]+nums[i] that can be stolen at present. If you don't steal, the maximum amount is from the last house. Because there is no change to room I, just refer to the previous one directly (because they are the largest).

③ Initialization dp[0] must be nums[0], but the second room must be selected from nums[0] and nums[1], because whether the second room is stolen or not will affect whether the first room is stolen or not. Just follow the recurrence above.

④ Traverse from bottom to top

The time complexity is n and the space complexity is n

However, it can be optimized into points. Two values are operated each time. dp[i-1] and dp[i-2] only need two temporary variables.

class Solution {
    public int rob(int[] nums) {
        if(nums.length==0) return 0;
        if(nums.length==1) return nums[0];
        
        // int[] dp=new int[nums.length];
        // dp[0]=nums[0];
        // dp[1]=Math.max(nums[0],nums[1]);
        int pre=nums[0];
        int cur=Math.max(nums[0],nums[1]);
        
        for(int i=2;i<nums.length;i++){
            int temp=Math.max(cur,pre+nums[i]);
            pre=cur;
            cur=temp;
            
        }
        return cur;
    }
}

2. House raiding 2

thinking

It's very similar to the original idea. The question is how to deal with the ring? In fact, there are two cases to deal with the ring. The first is to steal the first one, and the second is to steal the last one. If you steal the first one, you can't steal the last one. Similarly, if you steal the last one, you can't steal the first one. In other words, it is divided into two series to solve. In fact, it is to find the series of 0 – n-1 and 1 – n to rob houses. Suppose that the two points in the period, the K and k+1 points, because they are rings, are almost the same no matter who is regarded as the starting point. At this time, the recurrence is still the same, but divided into two sequences. But the problem is, if you want to steal the k+1 (equivalent to 0), is there a possibility that the K is not stolen? Then the result may be dp[k+1]=dp[k]+num. But the problem is that if dp[k] is not stolen, it is equal to the maximum amount of dp[k-1]. That is, dp[k+1]=dp[k]+num=dp[k-1]+num

① dp[i] means the biggest money you can steal from the first I room

② The recurrence is the same as above

③ Initialization is the same as above

④ Traverse from left to right

class Solution {
    public int rob(int[] nums) {
        if(nums.length==0) return 0;
        if(nums.length==1) return nums[0];
        
        return Math.max(robIt(Arrays.copyOfRange(nums,0,nums.length-1)),
        robIt(Arrays.copyOfRange(nums,1,nums.length)));
          
    }

    public int robIt(int[] nums){
        if(nums.length==0) return 0;
        if(nums.length==1) return nums[0];
        int pre=nums[0];
        int cur=Math.max(nums[0],nums[1]);
        
        for(int i=2;i<nums.length;i++){
            int temp=Math.max(pre+nums[i],cur);
            pre=cur;
            cur=temp;
        }
        return cur;
    }
}

3. Looting 3

dfs thinking

If you search on the tree, the possibility is to steal the grandfather node, so you can't steal the son node, but you can steal the grandson node. Or just steal the son node. To sum up, the choice for each node is either to steal itself and four grandchildren, or to steal only two son nodes. In fact, recursion to each node selection is the same, and finally find the largest of the two choices.

(timeout) time complexity is 6^n

class Solution {
    public int rob(TreeNode root) {
        if(root==null) return 0;
        
        int money=root.val;
        if(root.left!=null){
            money+=(rob(root.left.left)+rob(root.left.right));
        }

        if(root.right!=null){
            money+=(rob(root.right.left)+rob(root.right.right));
        }

        return Math.max(money,rob(root.left)+rob(root.right));

    }
}

Memory search idea

In fact, by recording the value of the node, it can prevent recursion when it is used next time, resulting in very high time complexity. For example, the grandparent node must have calculated the value of the grandson node. If it is not recorded, the value of the grandson node will be calculated again the next time the child node is calculated. The idea is basically the same.

class Solution {
    
    public int rob(TreeNode root) {
       Map<TreeNode,Integer> map=new HashMap<>();
       return robMem(root,map);
    }

    public int robMem(TreeNode root,Map<TreeNode,Integer> map){
         if(root==null) return 0;
        if(map.containsKey(root)) return map.get(root);
        int money=root.val;
        if(root.left!=null){
            money+=(robMem(root.left.left,map)+robMem(root.left.right,map));
        }

        if(root.right!=null){
            money+=(robMem(root.right.left,map)+robMem(root.right.right,map));
        }
        int max=Math.max(money,robMem(root.left,map)+robMem(root.right,map));
        map.put(root,max);
        return map.get(root);
    }
}


Dynamic programming idea

This idea can not continue the previous definition of memory search, that is, it can not calculate grandson nodes while calculating grandparent nodes, which consumes a lot of performance. So what about tree dynamic programming? If you want to know whether the root node steals the most, do you first need to know whether the grandson node or the son node steals the most? If so, it is obvious that only the lowest node can push back the maximum number of stolen root nodes. So this place uses post order traversal. The question comes again. What should each node do?

① If this node is not stolen, the son node can steal or not. That is, the sum of the largest stolen money returned from the son node is the largest stolen amount of this node.

② If you steal this node, the maximum stolen amount that the child node cannot steal + the money of this node

The question is how to store? What is the return value?

In the first case, the son node needs two possible return values. The first is stolen and the second is not stolen. That is, each node returns an array, including stolen and not stolen. Then the maximum stolen money of the left and right child nodes can be obtained when calculating the results (both stealing and not stealing need to be considered)

class Solution {
    public int rob(TreeNode root) {
        int[] res=robIt(root);
        return Math.max(res[0],res[1]);
    }

    public int[] robIt(TreeNode root){
        if(root==null) return new int[2];
        int[] res=new int[2];
        
        int[] left=robIt(root.left);
        int[] right=robIt(root.right);

       
        res[0]=Math.max(left[0],left[1])+Math.max(right[0],right[1]);
        res[1]=left[0]+right[0]+root.val;
        return res;
     }
}

Posted by ehask on Sun, 24 Oct 2021 03:18:56 -0700