题目
在上次打劫完一条街道之后,窃贼又发现了一个新的可以打劫的地方,但这次所有的房子围成了一个圈,这就意味着第一间房子和最后一间房子是挨着的。每个房子都存放着特定金额的钱。你面临的唯一约束条件是:相邻的房子装着相互联系的防盗系统,且 当相邻的两个房子同一天被打劫时,该系统会自动报警。
给定一个非负整数列表,表示每个房子中存放的钱, 算一算,如果今晚去打劫,你最多可以得到多少钱 在不触动报警装置的情况下。
注意事项
这题是House Robber的扩展,只不过是由直线变成了圈
给出nums = [3,6,4]
, 返回 6
, 你不能打劫3
和4
所在的房间,因为它们围成一个圈,是相邻的.
分析
(1)假设打劫 House 0,则 House 1 和 House n-1 都不能打劫,此时变成 House 2 ~ House n-2 间的无循环动态规划问题
(2)假设不打劫 House 0,则 House 1 和 House n-1 随便打不打劫,此时变成 House 1 ~ House n-1 间的无循环动态规划问题
(3)假设House s 与 House e 之间的动态规划问题
假设对于 s, e 之间的某个House k
假设 dp[k] 的含义:走过 House k 后,小偷能获得的最大钱数
dp[k]有两个来源:
1)打劫 House k,resa = dp[k-2]+nums[k]
2)不打劫 House k,resb = dp[k-1]
则 dp[k] = max(resa, resb)
代码
public class Solution {
/**
* @param nums: An array of non-negative integers.
* return: The maximum amount of money you can rob tonight
*/
public int houseRobber2(int[] nums) {
// write your code here
if(nums==null || nums.length==0) return 0;
int n = nums.length;
int[] dp = new int[n];
if(n==1) return nums[0];
//偷index=0的情况
//则index=n-1不能偷;index=1不能偷
//index=2~n-2,和一条街的情况一致
int resa=0;
if(n>3){
resa=nums[0]+helper(nums,2,n-2);
}
//不偷index=0的情况
//index=1~n-1,和一条街的情况一致
int resb=0;
if(n>1){
resb=helper(nums,1,n-1);
}
return Math.max(resa,resb);
}
int helper(int[] nums, int s, int e){
int len=e-s+1;
if(len==1) return nums[s];
int[] dp = new int[len];
dp[0]=nums[s];
dp[1]=Math.max(nums[s],nums[s+1]);
int k=2;
for(int i=s+k; i<=e; k++,i++){
dp[k]=Math.max(dp[k-2]+nums[i],dp[k-1]);
}
return dp[len-1];
}
}