你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例 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 。
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/house-robber
public int rob(int[] nums) {
int n = nums.length;
if(n == 0) return 0;
int[] amount = new int[n + 1]; // amount[i]的意义是截至第i家,能偷的最多的钱为amount[i]
amount[0] = 0;
amount[1] = nums[0];
for(int i = 2; i <= n; i++){
amount[i] = Math.max(amount[i - 1], amount[i - 2] + nums[i - 1]);
}
return amount[n];
}
思路分析:
- 限制条件,不能偷连续的两家。本题要求出窃取的最高金额,是一个最优问题。可以尝试能不能使用动态规划。
- 最后能偷到的最大金额,最后一家偷还是不偷呢?这取决于截至倒数第二家能偷到的最多金额以及截至倒数第三家能偷到的最多的金额。如果最后一家的钱加上截至倒数第三家的偷取的金额比截至倒数第二家能偷到的最多金额大,那么小偷就选择倒数第二家不偷。否则不如最后一家放弃,拿着截至倒数第二家的金额走。
- 所以存在一个最优子结构,要得到截至第
i
家最多能偷取多少钱,就得先知道截至第i-1
与截至第i-2
家最多能偷取多少钱。所以可以确定问题的状态amount[i]
表示截至第i
家,能偷的最多的钱。 - 下一个问题就是确定状态转移方程,从前面提到的小偷的决策逻辑可以知道,转移方程
amount[i] = Math.max(amount[i - 1], amount[i - 2] + nums[i - 1]);
- 确定计算方向,很明显要先知道前面的情况,所以是从小到大进行计算。边界条件的确定需要看哪些元素无法通过状态转移方程得到。第0家不存在,所以
amount[0] = 0
;截至第一家只能偷这一家的钱,所以amount[1] = nums[0]
。 - 根据状态定义,最终我们要求的就是
count[n]
。 - 只进行了单层遍历,所以时间复杂度为$O(n)$,空间复杂度也为$O(n)$。当然空间复杂度可以做到$O(1)$,只需要两个辅助变量即可。
运行结果:
- 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户
- 内存消耗 :37 MB, 在所有 Java 提交中击败了5.21%的用户