题目:
题目分析:
看完题目,我们可以发现,取了某数字的值那么和该数字相邻(x-1和x+1)的数则都不能取,是不是很像打家劫舍,只是单个数字可能是不相邻的,但如果把他们看成一堆数字和出现次数组成的序列,如1:1,2:3,3:3那么我们完全就可以看作打家劫舍问题了,当我们取1就不能取2,取1 的所有结果就是1*1,同理取2不能取1和3,结果是2*3。
我们把这样的次数*数字看作打家劫舍中每栋房屋的总金额,而用数字来区分房屋是否相邻,这就成了当前房屋偷与不偷的选择关系了。则可得到打家劫舍中偷n栋房屋最大值的dp关系:
d p [ n ] = m a x ( n u m s [ n ] + d p [ n − 2 ] , d p [ n − 1 ] ) dp[ n ] = max(nums[ n ]+dp[n-2],dp[n-1]) dp[n]=max(nums[n]+dp[n−2],dp[n−1])
题解一(建立一次性连续的房屋)
class Solution {
public:
int deleteAndEarn(vector<int>& nums) {
//得到所有数字中的最大值,用于标记最大有多少个房屋
int max_val = *max_element(nums.begin(),nums.end());
vector<int>house(max_val+1);
//把对应标号里面的房子装进对应的财产
for(int val:nums){
house[val] += val;
}
return rob(house);
}
private:
//用于处理打家劫舍问题的函数
int rob(vector<int>&house){
if(house.size()<2)
return house[0];
//一栋房的dp结果
int pre = house[0];
//两栋房的dp结果
int cur = max(house[0],house[1]);
//由于dp表达式中只和前一次dp以及当前的dp有关,完全可用两个变量表示
for(int i=2;i<house.size();i++){
//要么偷当前房屋,就是偷前面n-2栋房屋的情况+nums[n],不偷,则就是偷前面n-1栋房屋的情况
//哪种情况下的值大则就是该栋情况下的取值。
int temp = cur;
cur = max(house[i]+pre,cur);
//注意prv要进行更新迭代
pre = temp;
}
return cur;
}
};
题解二(一次遍历解决,建立非连续的多个连续房屋)
由于打家劫舍问题是需要房屋连续的情况,比如之前即便,有未连续的数字,那其中的金额也可以设为0来解决。而我们现在这个方法就可以不用为了建立一个连续的房屋来浪费额外的空间了。
为了建立多个连续的房屋,只能利用先排序来解决。
且由于不用对应的数字标记房屋,转而用数组本来的下标标记房屋,这样只能通过排序来分散建立多个独立的房屋来实施偷盗才能如此利用。
详情见代码:
class Solution {
public:
int deleteAndEarn(vector<int>& nums) {
sort(nums.begin(),nums.end());
vector<int>house = {nums[0]};
int res = 0;
//把对应标号(该标号不是用对应的数字来表示了,直接用house的数字下标,当不连续时重新处理house数组)里面的房子装进对应的财产,并且当不连续(两者标号超过1)时分割。
//这里的一切都建立在排序的基础上,因为只有排序后才会出现相同数字的连续。
for(int i=1;i<nums.size();i++){
//当相同时,则填入房屋当作财产
if(nums[i]==nums[i-1]){
house.back() += nums[i];
}
//当相差为1时,新建一个标号连续的房屋
else if(nums[i]==nums[i-1]+1){
house.emplace_back(nums[i]);
}
//当相差超过1,则该数字对前面的序列不会产生影响,故建立新的房屋序列(建立前先把前面的房屋钱财偷完)
else{
res += rob(house);
house = {nums[i]};
}
}
//循环后,最后肯定会漏掉一个
res += rob(house);
return res;
}
private:
//用于处理打家劫舍问题的函数
int rob(vector<int>&house){
if(house.size()<2)
return house[0];
//一栋房的dp结果
int pre = house[0];
//两栋房的dp结果
int cur = max(house[0],house[1]);
//由于dp表达式中只和前一次dp以及当前的dp有关,完全可用两个变量表示
for(int i=2;i<house.size();i++){
//要么偷当前房屋,就是偷前面n-2栋房屋的情况+nums[n],不偷,则就是偷前面n-1栋房屋的情况
//哪种情况下的值大则就是该栋情况下的取值。
int temp = cur;
cur = max(house[i]+pre,cur);
//注意prv要进行更新迭代
pre = temp;
}
return cur;
}
};
理论上来说解法一应该要快一点(因为排序的时间复杂度),实际情况是差不多的。。