House Robber II

House Robber II

问题:

Note: This is an extension of House Robber.

After robbing those houses on that street, the thief has found himself a new place for his thievery so that he will not get too much attention. This time, all houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, the security system for these houses remain the same as for those in the previous street.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

思路:

  动态规划

我的代码1:

public class Solution {
    public int rob(int[] nums) {
            if(nums == null || nums.length == 0)    return 0;
            int len = nums.length;
            if(len == 1) return nums[0];
            if(len == 2) return Math.max(nums[0],nums[1]);
            int max = Integer.MIN_VALUE;
            
            for(int i=0; i<len; i++)
            {
                if(len == 3)
                    max = Math.max(max,nums[i]);
                else if(len == 4)
                    max = Math.max(max, nums[i]+nums[(i+2)%len]);
                else if(len == 5)
                    max = Math.max(max, nums[i]+Math.max(nums[(i+2)%len], nums[(i+3)%len]));
                else
                {
                    int[] result = new int[len-3];
                    int k = 0;
                    result[k++] = nums[(i+2)%len];
                    result[k++] = Math.max(nums[(i+2)%len],nums[(i+3)%len]);
                    for(int j = (i+4)%len; j != (i-1+len)%len; j = (j+1)%len,k++)
                    {
                        result[k] = Math.max(result[k-1],nums[j]+result[k-2]);
                    }
                    max = Math.max(max, result[len-4]+nums[i]);
                }
            }
            return max==Integer.MIN_VALUE?0:max;
        }
}
View Code

我的代码2:

public class Solution {
    public int rob(int[] nums) {
            if(nums == null || nums.length == 0)    return 0;
            int len = nums.length;
            if(len == 1) return nums[0];
            if(len == 2) return Math.max(nums[0],nums[1]);
            return Math.max(rob(nums, 1, 0), rob(nums, 2, 1));
     }
    public int rob(int[] nums, int l, int h) {
        int len = nums.length;
        int[] result = new int[len-1];
        int k = 0;
        result[k++] = nums[l];
        result[k++] = Math.max(nums[(l+1)%len],nums[l]);
        for(int j=(l+2)%len; j!=h; j=(j+1)%len,k++)
        {
            result[k] = Math.max(result[k-1],nums[j]+result[k-2]);
        }
        return result[len-2];
    }
}
View Code

他人代码:

public class Solution {
    public int rob(int[] nums) {
            if(nums == null || nums.length == 0)    return 0;
            if(nums.length == 1)    return nums[0];
            int n = nums.length;
            return Math.max(robHelper(Arrays.copyOfRange(nums,0,n-1)),robHelper(Arrays.copyOfRange(nums,1,n)));
        }
       public int robHelper(int[] nums) {
        if(nums==null || nums.length==0)    return 0;
        if(nums.length == 1)    return nums[0];
        int len = nums.length;
        int[] max = new int[len];
        max[0] = nums[0];
        max[1] = Math.max(nums[0],nums[1]);
        
        for(int i=2; i<len; i++)
        {
            max[i] = Math.max(max[i-1], nums[i]+max[i-2]);
        }
        
        return max[len-1];
    }
}
View Code

学习之处:

  • 代码的思想都是把圈的问题划分成直线的问题,第一次代码考虑的思路是划分圈的方式是尝试是否取每一个节点,取这个节点的时候划分圆圈为直线。时间复杂度是O(n*n),考虑的过程是01背包的思想
  • 第二次代码的思路是划分圈的方式是第一个和第二个节点必定有一个是最后不取的,所以按照这个理论可以不取第一个节点然后划圈为直线,或者不取第二个节点划圈为直线(好思路啊,看了别人的解释才恍然大悟),时间复杂度是O(n)
  • result[k++]方式尽量少用,用着用着就不知道什么地方出错了,为了cool代码出错可不值得。
  • 逆向思维不仅取决于递归转换成动态规划,逆向思维也表现在一个思路是尝试取这个节点看看是否合适,然后再依次遍历尝试其他节点,另外一个思路便是必有一个节点不取。
  • 他人的思路真是精妙,既然0和n-1在循环的条件下不能同时取到,那么该问题可以转换成等价的两个子问题,分别是0到n-2和1到n-1二者最大值即为要求的结果,II问题总是可以在I问题上进行发挥,也就是可以通过I问题可以解决II问题,学会进行复用!!!
原文地址:https://www.cnblogs.com/sunshisonghit/p/4534787.html