打家劫舍2
力扣原题链接
问题描述
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下,今晚能够偷窃到的最高金额。
示例
示例 1:
输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:
输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。偷窃到的最高金额 = 1 + 3 = 4 。
示例 3:
输入:nums = [1,2,3]
输出:3
分析
这是一个环形动态规划问题。由于第一个房屋和最后一个房屋是相邻的,因此偷窃第一个房屋就不能偷窃最后一个房屋,偷窃最后一个房屋就不能偷窃第一个房屋。因此,我们可以将问题转化为两个子问题:
- 不偷窃第一个房屋,偷窃第二个房屋到最后一个房屋之间的房屋。
- 不偷窃最后一个房屋,偷窃第一个房屋到倒数第二个房屋之间的房屋。
对于每个子问题,可以使用动态规划求解。
状态定义
定义一个一维动态规划数组 dp
,其中 dp[i]
表示偷窃第一个房屋到第 i
个房屋时的最高金额。
状态转移方程
对于每个房屋 nums[i]
,有两种选择:偷窃该房屋或者不偷窃该房屋。状态转移方程为:
dp[i] = max(dp[i-1], dp[i-2] + nums[i])
初始化
需要对动态规划数组进行初始化。初始时,dp[0] = nums[0]
,dp[1] = max(nums[0], nums[1])
。
Java解题
class Solution {// 主函数,计算环形盗窃计划的最大金额public int rob(int[] nums) {// 如果只有一个房屋,则直接返回该房屋的金额if (nums.length == 1) return nums[0];// 分别计算不偷窃第一个房屋和不偷窃最后一个房屋的最大金额,取两者的较大值return Math.max(robRange(nums, 0, nums.length - 2), robRange(nums, 1, nums.length - 1));}// 辅助函数,计算偷窃指定范围内房屋的最大金额private int robRange(int[] nums, int start, int end) {// 初始化前一个房屋的最大金额和当前房屋的最大金额int prevMax = 0;int currMax = 0;// 遍历指定范围内的房屋for (int i = start; i <= end; i++) {// 临时保存当前房屋的最大金额int temp = currMax;// 更新当前房屋的最大金额,选择偷窃当前房屋或者不偷窃当前房屋currMax = Math.max(prevMax + nums[i], currMax);// 更新前一个房屋的最大金额prevMax = temp;}// 返回偷窃指定范围内房屋的最大金额return currMax;}
}
总结
通过环形动态规划的思想,我们可以解决这个问题。首先将问题分解为两个子问题,然后对每个子问题使用动态规划求解,最终返回两个子问题的最大值。