【Leetcode】213. 打家劫舍 II

107 阅读2分钟

题目描述

在这里插入图片描述

// 213. 打家劫舍 II

// 你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这
// 个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨
// 着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同
// 一晚上被小偷闯入,系统会自动报警 。

// 给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的
// 情况下 ,今晚能够偷窃到的最高金额。


题解

// 动态规划
// 跟【Leetcode】198. 打家劫舍 基本上很像,但是根据题意,第一个跟最后一个
// 元素是连起来的。所以如果取最后一个值的时候,第一个值也不能要了,
// 如果要第一个值,那最后一个值也不能要了。
// 
// 本题可以拆解为两个【Leetcode】198. 打家劫舍 问题,第一个从左到右打家劫舍,
// 一直遍历到length - 2位置,第二个从右到左打家劫舍,一直遍历到1位置。
// 我们定义subRob,实际上就是把【Leetcode】198. 打家劫舍 包装一下。
// 
// 执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
// 内存消耗:35.7 MB, 在所有 Java 提交中击败了87.92%的用户
class Solution {
    public int rob(int[] nums) {
        int len = nums.length;
        if (len == 0)
            return 0;
        else if (len == 1)
            return nums[0];

		int[] dp = new int[len];
		int res1 = subRob(nums, dp, 0, 1, len - 2);
		int res2 = subRob(nums, dp, len - 1, len - 2, 1);
		return Math.max(res1, res2);
	}
	
	public int subRob(int[] nums, int[] dp, int start1, int start2, int end) {
		dp[start1] = nums[start1];
		dp[start2] = Math.max(dp[start1], nums[start2]);
		if (start1 < end) {
			for (int i = start2 + 1; i <= end; i++) {
				dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]);
			}
		}
		else if (start1 > end) {
			for (int i = start2 - 1; i >= end; i--) {
				dp[i] = Math.max(dp[i + 2] + nums[i], dp[i + 1]);
			}
		}
		return dp[end];
	}
}




// 简写一下
// 可以不用真的反方向dp,只要一个从0开始dp到倒数第二个,一个从1开始dp倒数
// 到倒数第一个。重新封装subRob,只需要保留从左到右的遍历,
// 注意排查特殊情况,比如[0,0]这种情况,start2根本取不到,会抛出异常,
// 所以在start1初始化之后,我们要验证一下start1和end是不是相等,如果是
// 直接返回dp[end]。
// 
// 执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
// 内存消耗:35.6 MB, 在所有 Java 提交中击败了96.11%的用户
class Solution {
    public int rob(int[] nums) {
        int len = nums.length;
        if (len == 0)
            return 0;
        else if (len == 1)
            return nums[0];

		int[] dp = new int[len];
		int res1 = subRob(nums, dp, 0, 1, len - 2);
		int res2 = subRob(nums, dp, 1, 2, len - 1);
		return Math.max(res1, res2);
	}

	public int subRob(int[] nums, int[] dp, int start1, int start2, int end) {
		dp[start1] = nums[start1];
		if (start1 == end) return dp[end];
		dp[start2] = Math.max(dp[start1], nums[start2]);
        for (int i = start2 + 1; i <= end; i++) {
            dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]);
        }
		return dp[end];
	}
}