题目来源
https://leetcode.com/problems/house-robber-ii/description/
题目理解
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.
题意其实比较的简单. 即有环状排列的一圈房子, 每间房子里都有一定数额的金币. 作为一个小偷, 我的任务就是尽可能多的偷到钱. 但是要注意到做贼心虚, 一个贼他必定不能引起警察的注意–而一旦偷了相邻的房子的财物, 警报系统就会发出警报.
所以问题已经清晰–给出一个数列(首尾相连), 在不能取连续的两个值的前提下, 使得它的一个部分和最大.
解题思路
和这道题的初始版本类似, 这道题本质上也是一个类动态规划问题. 我们不妨先考虑上一道题的初始版本–即在数组不是环形, 而是线形条件下的解法.
初始问题实际就是一个简单的动态规划的变种–变就变在我们需要保存两个状态.
这道题Bellman Equation为:
当前i能抢到的最多金额=max(抢第i间房子所能得到的最大金额,不抢第i间房子所能得到的最大金额)
当
前
i
能
抢
到
的
最
多
金
额
=
m
a
x
(
抢
第
i
间
房
子
所
能
得
到
的
最
大
金
额
,
不
抢
第
i
间
房
子
所
能
得
到
的
最
大
金
额
)
抢第i间房子的最大金额=不抢第i−1间房子的最大金额+第i间房子的金额
抢
第
i
间
房
子
的
最
大
金
额
=
不
抢
第
i
−
1
间
房
子
的
最
大
金
额
+
第
i
间
房
子
的
金
额
不抢第i间房子的最大金额=max(抢第i−1间房子的最大金额,不抢第i−1间房子的最大金额)
不
抢
第
i
间
房
子
的
最
大
金
额
=
m
a
x
(
抢
第
i
−
1
间
房
子
的
最
大
金
额
,
不
抢
第
i
−
1
间
房
子
的
最
大
金
额
)
根据以上3个式子写出代码即可快速求解.
int rob_1(int start, int end, const vector<int>& nums) {
int pre = 0;
int cur = 0;
int temp;
for (int count = start; count < end; count++) {
temp = pre;
pre = cur;
cur = temp + nums[count] > cur ? nums[count] + temp : cur;
}
return cur;
}
那么现在问题来了, 如何解决这一问题的进阶版本? 即是一个环形的数组(房子). 我们考虑能否把大问题分解成两个小问题.
注意到对于一间特定的房子, 只有两种可能–偷还是不偷. 只要对于圈状的房子圈中的某两间连续的房子, 我们分别考虑偷房子1而不偷房子2, 偷房子2而不偷房子1这两种情形下问题的解, 就相当于考虑了所有的情况 因此, 这样一来, 我们就把一个圈的问题拆成了两个线的问题–即是两个问题1–House Robber.
代码实现
#include <vector>
using namespace std;
class Solution {
public:
int rob(vector<int>& nums) {
if (nums.size() == 0) return 0;
if (nums.size() == 1) return nums.front();
int not_last = rob_1(0, nums.size() - 1, nums);//不偷最后一间房子
int not_first = rob_1(1, nums.size(), nums);//不偷第一间房子
return not_last > not_first ? not_last : not_first;//两者间的较大值即是整个问题的解
}
\\简单情况(首尾不相接情况下问题的解)
int rob_1(int start, int end, const vector<int>& nums) {
int pre = 0;
int cur = 0;
int temp;
for (int count = start; count < end; count++) {
temp = pre;
pre = cur;
cur = temp + nums[count] > cur ? nums[count] + temp : cur;
}
return cur;
}
};
代码表现: