1. 打家劫舍
某大厂 2022 年 9 月面试题挑战(三)
1.1 题目描述
力扣真题地址:https://leetcode.cn/problems/house-robber/?envType=study-plan-v2
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 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 。
提示:
- 1 <= nums.length <= 100
- 0 <= nums[i] <= 400
1.2 题目解析
可以先简单理解为求数组的子数组的最大和问题,其中子数组满足条件:子数组中所有元素在原数组中不相连。比如这种情况下 [1, 99, 99, 1] 那么只能得到最大值为 100,不能 99 + 99 。
示例 3
输入:[1,99,99,1]
输出:100
接着需要思考的是,数组中最大的数一定会选吗 ?很明显不是的,需要考虑选中的这个数的相邻之和是否更加大,即选中这个数是否会付出更大的代价。
示例 4
输入:[54,100,99,1]
输出:153
是否就选择两两间隔即可,比如选择房间 1 3 5 7 9 这种 ? 肯定也是不妥的,比如下面输出。
示例 5
输入:[54, 1, 1, 54]
输出:108
所以,在选择房屋的过程中,需要考虑 “得失” 的问题,即风险是否大于收益问题。这里我们使用一个变量存档,另外一个变量大步向前,如果发现 “此路不通”,就可以回到历史版本再次出发。
1.3 解题图示
这里绘制了一个简单的图示,请结合代码对解题思路进行梳理。
对应的源码如下:
class Solution {
public:
int rob(vector<int>& nums) {
int sum1 = 0, sum2 = 0;
for (int num : nums) {
int tmp = sum2;
sum2 = max(sum1 + num, sum2);
sum1 = tmp;
}
return sum2;
}
};
时间复杂度: O(n)
空间复杂度: O(1)
1.4 官方参考解答
上述方法应该应该是最容易想到的方法,肯定会有很多大神给出其他解题思路,这里也前去查看一下。
我自己与官网教程的解题方法是一致的,但是对变量的称呼不同,官网考虑每个房间的 “偷” 与 “不偷” 两种情况,而我考虑到的是 “历史版本” 这个概念,其实是同一个意思,我更加强调的是 “后悔” 的话就可以 “rollback” 随时回滚。
1.5 其他优质思路
暂无
1.6 总结
这应该算是一个比较容易的动态规划问题,但是如果陷入某个死胡同一时半会也解不出来。整个思考的过程应当是首先意识到这是一个动态规划的问题,然后考虑到两个版本的规划问题,因为偷窃的两个房屋不能相邻,所以每一间房间只有两种可能 —— 偷 与 不偷,我们使用一个中间变量记录一下,以便于 “后悔” 的时候可以回滚。
Smileyan
2023.05.01 17:43