408算法练习——打家劫舍(动态规划)
打家劫舍(动规)
题目链接:https://leetcode-cn.com/problems/house-robber-ii/
一、问题描述
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
示例 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 = [0]
输出:0
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 1000
二、问题分析
因为所有房屋是成环状的,也就是说如果去了第一间,就不能去最后一间,如果去了最后一间,就不能去第一间;那么可见简单的将这个数组拆分成两个数组(只需要在逻辑上进行拆分即可)第一个数组不包含最后一个元素,第二个数组不包含第一个元素。最后我们只需要判定两种方式,哪种获得的利益最大,返回最大的利益即可。对于特殊情况,只有一间或只有两间房屋,可以用一个判断来跳过,这样能省不少事。
下面是分析拿钱的过程(针对以上两种数组都是一样的),给每个房子一个门牌号,假设小偷此时经过7号房子,那么如果他要拿7号房子的钱,他就不能拿6号和8号房子里的钱,但是他可以拿5号房子里的钱。所以这个问题的模型就被简化成了:当指针遍历到一个元素n时,如果n的值加n-2的值,比n-1大,那么应该保留n-2。反之我们保留n-1。
所以可行创建出一个动态规划数组dp,dp[i]的值表示了当小偷经过i号房屋时能偷到的最大金额。
为了降低空间复杂度,小偷应该尝试在原数组上进行修改,但是我们又不能修改原数组,因为要进行两次遍历;因为每次的判断其实都只是对三个元素进行判断,那么就定义三个变量来实现这个功能。
三、代码
关于one和two,你可以把他想象成一个窗口,窗口中包含要更新的动态数组dp[i]以及他的前两个元素。每次移动窗口就是在重新对one和two赋值的过程
1 class Solution { 2 public int rob(int[] nums) { 3 if(nums.length == 1 ){ 4 return nums[0]; 5 } 6 if(nums.length == 2){ 7 return Math.max(nums[0],nums[1]); 8 } 9 return Math.max(getmoney(nums,0,nums.length-2),getmoney(nums,1,nums.length-1)); 10 } 11 //动规数组三个变量分别是数组,数组的上下界 12 public int getmoney(int[] nums,int start,int finish){ 13 int one = nums[start],two=Math.max(nums[start],nums[start+1]);//两个变量记录上次循环的结果 14 int temp = 0;//因为要顺次移动one和two所以需要一个中间元素 15 for(int i=start+2;i<=finish;i++){ 16 temp = two; 17 two = Math.max(nums[i]+one,two); 18 one = temp; 19 } 20 return two; 21 } 22 }

浙公网安备 33010602011771号