一般的DP问题都是基于非循环数组的,但今天在做LeetCode的抢劫问题(213 House Robber II)时,遇到了形成环的数列问题。
之前在加油问题中也遇到类似的,今天在这里就分析一下循环数组的动态规划问题。
先看题目描述:
213 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)第一座房子不抢; 2)第一座房子抢。(这里编号从1开始)
只要分别求出这两种情况所能获得的最多钱,返回两者最大的就是答案了。
动态规划数组还是长度为nums.length + 1,多出来的1是为了方便计算。
dp[0 : nums.length], 其中dp[0] = 0;
dp[i] 代表前 i 座(1,2,...,i)房子所能获得最大值
递推式为dp[i] = max(dp[i - 1], dp[i - 2] + nums[i - 1])
1)第一种情况时,dp[1] = 0; // 不抢第一座房子
接着根据递推式开始计算dp[2...nums.length]// 最后一座可能抢,所以要计算
记录第一种情况的结果res1 = dp[nums.length]
2)第二种情况时,dp[1] = nums[0]; // 第一座房子必抢
接着根据递推式开始计算dp[2...nums.length - 1]// 最后一座不抢,所以不计算
记录第二种情况的结果res2 = dp[nums.length - 1]
最后的结果就是max(res1, res2)
接下来当然是
Show me the code!
public int rob(int[] nums) { if(nums == null || nums.length == 0) return 0; if(nums.length == 1) return nums[0]; int res = 0; int[] dp = new int[nums.length + 1]; dp[0] = 0; dp[1] = 0; // 第1个不抢 for(int i = 2; i <= nums.length; i++){ dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i - 1]); } res = dp[nums.length]; dp[0] = 0; dp[1] = nums[0];// 第1个抢 for(int i = 2; i < nums.length; i++){ dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i - 1]); } res = Math.max(res, dp[nums.length - 1]); return res; }