题目描述
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
样例描述
示例 1:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
思路
- 动态规划。先进行状态表示,如下:
- 状态计算
方法二: 仅用一个数组dp表示。
dp[i]表示前i间房屋能偷到的最高金额
方法三:空间复杂度到O(1)
由于每时刻的最大偷窃数,在逐步累加过程中只与前两个房间相关,因此可以用滚动数组的思想,只存储两个变量即可
代码
方法一:
class Solution {
public int rob(int[] nums) {
int n = nums.length;
int f[] = new int[n + 1]; //f[i]表示 偷 第i个房子
int g[] = new int[n + 1]; //g[i]表示 不偷 第i个房子
for (int i = 1; i <= n; i ++ ) {
//偷第i个就肯定不偷第i - 1个, 细节这里第i个房子对应nums[i - 1]!
f[i] = g[i - 1] + nums[i - 1];
//不偷第i个的话有两种: 第i - 1个偷 或者不偷 取其中较大的方案
//例如若i 指向18。 序列 9 1 1 18就是i - 1也不偷
g[i] = Math.max(f[i - 1], g[i - 1]);
}
//返回较大的即可
return Math.max(f[n], g[n]);
}
}
方法二:
class Solution {
public int rob(int[] nums) {
int n = nums.length;
if (n == 0 || nums == null) return 0;
if (n == 1) return nums[0];
int dp[] = new int[n + 1];
dp[0] = nums[0]; //dp[i]表示前i间房子能偷到的最大值
dp[1] = Math.max(nums[0], nums[1]);
for (int i = 2; i < n; i ++ ) {
//方案一:偷第i间就不能偷第i - 1间,也就是前i - 2间的和 加上 第i间
//方案二:不偷第i间,dp[i - 1] 就是前i - 1间的最大值
dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]);
}
return dp[n - 1];
}
}
方法三
class Solution {
public int rob(int[] nums) {
int n = nums.length;
if (n == 0 || nums == null) {
return 0;
}
if (n == 1) {
return nums[0];
}
int p = nums[0], q = Math.max(p, nums[1]);
//滚动数组
for (int i = 2; i < n; i ++ ) {
int t = q;
q = Math.max(q, p + nums[i]);
p = t;
}
return q;
}
}