本文正在参与掘金团队号上线活动,点击 查看大厂春招职位
一、题目描述:
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
示例 1:
输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
复制代码
二、思路分析:
因为不能选择相邻的房屋,一种简单的推测是只偷奇数或者只偷偶数号房屋,但是很可惜这种推测是错误的,因为除了间隔一间房屋来偷,还可以间隔两间房屋来偷,并不是每走两步就必须偷一间,而使用动态规划思想就可以得到最优解了。
假设我们是从前往后依次来偷的,那么当我们位于第 间房屋的时候,偷不偷 号房只和前面位于前面两间房屋的最大收益有关,一种可能是偷了 号房 并且偷了 号房,还有一种可能是偷了 号房,就不能偷 号房了。
让我们用 表示在 号房的最大收益,那么很容易地写出状态方程:,然后注意一下边界情况就行。因为这道题还有一个限制条件:第一个和最后一个房屋是相邻的,偷了第一间就不能偷最后一间,所以我们需要分情况求解选最大值。
三、AC 代码:
class Solution {
public:
int rob(vector<int>& nums) {
int n = nums.size();
if (n == 1) {
return nums[0];
}
if (n == 2) {
return max(nums[0], nums[1]);
}
vector<int> dp(n);
dp[0] = nums[0], dp[1] = max(nums[0], nums[1]);
for (int i = 2; i < n - 1; ++i) {
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i]);
}
int ans = dp[n - 2];
dp[1] = nums[1], dp[2] = max(nums[1], nums[2]);
for (int i = 3; i < n; ++i) {
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i]);
}
return max(ans, dp[n - 1]);
}
};
复制代码
四、总结:
在解决一些分步的最优化题目时,动态规划往往是个不错的选择,因为动态规划能有效降低时间复杂度,而且有时所产生的的额外空间复杂度也能得到优化,比如这道题就可以使用滚动数组来替代整个 数组将空间复杂度降至 ,感兴趣的同学可以尝试一下。