题目描述:给你一个整数数组 nums ,你可以对它进行一些操作。每次操作中,选择任意一个 nums[i] ,删除它并获得 nums[i] 的点数。之后,你必须删除 所有 等于 nums[i] - 1 和 nums[i] + 1 的元素。开始你拥有 0 个点数。返回你能通过这些操作获得的最大点数。

方法1:动态规划,如果上一次选择的点数和这一次一样(这一次也直接取),如果上一次选择的点数比这一次只小1(取上一次与上t次的最大值,上t次刚好和上一次不一样),不仅仅只小1(也直接取)

class Solution {
public:
    int deleteAndEarn(vector<int>& nums) {
        vector<int>dp(nums.size()+1); //前i个数能获得的最大点数
        sort(nums.begin(),nums.end());
        dp[0] = 0;
        dp[1] = nums[0];
        int t=1;

        for(int i=1;i<nums.size();i++){
            if(nums[i] == nums[i-1]){
                dp[i+1] = dp[i] + nums[i];
                t++;
            }
            else if(nums[i] == nums[i-1] +1){
                dp[i+1] = max(dp[i],dp[i-t]+nums[i]);
                t = 1;
            }
            else dp[i+1] = dp[i] + nums[i];
        }
        return dp[nums.size()];
    }
};

出错,第一个if循环错误,nums[i-1]不一定是上一次选择的点数,如果(2,2,3,3,3)应该是9,以这个逻辑却是10,取了两个2后,第一个3会舍弃,但是第二个3与第三个3会任务上一次选择的是3,从而会直接取

解决办法:用一个哈希表记录每一个数在数组中出现的次数

class Solution {
public:
    int deleteAndEarn(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        unordered_map<int,int>count;//每个不一样的nums[i]出现的次数
        for(int i=0;i<nums.size();i++){
            count[nums[i]]++;
        }
        
        vector<int>dp(count.size()+1); //前i个数能获得的最大点数
        auto p = count.begin();
        auto q = count.begin();
        dp[0] = 0;
        dp[1] = (p->first) * (p->second);

        for(int i=2;i<count.size()+1;i++){
            q++;
            if(p->first == q->first-1) dp[i] = max(dp[i-1],dp[i-2] + q->first * q->second);
            else dp[i] = dp[i-1] + q->first * q->second;
        }
        return dp[count.size()];
    }
};

出错,哈希表中key的顺序并不是存放的先后顺序,q->first与p->first在排序后的nums[i]中并不一定是靠在一起的

解决办法,用一个数组存放key,再对数组排序

class Solution {
public:
    int deleteAndEarn(vector<int>& nums) {
        unordered_map<int,int>count;//每个不一样的nums[i]出现的次数
        vector<int> single_num;
        for(int i=0;i<nums.size();i++){
            count[nums[i]]++;
        }
        for(auto it = count.begin();it!=count.end();it++) single_num.push_back(it->first);//用一个数组存放key
        sort(single_num.begin(),single_num.end());

        vector<int>dp(single_num.size()+1); 
        dp[0] = 0;
        dp[1] = single_num[0] * count[single_num[0]];

        for(int i=2;i<single_num.size()+1;i++){
            if(single_num[i-1] == single_num[i-2]+1) dp[i] = max(dp[i-1],dp[i-2] + single_num[i-1] * count[single_num[i-1]]);
            else dp[i] = dp[i-1] + single_num[i-1] * count[single_num[i-1]];
        }
        return dp[single_num.size()];
    }
};

方法2:将题目改造成打家劫舍,设max_val是nums数组中最大的数,设有max_val间房子,第val个房子存的钱为(val) * (val出现的次数)

class Solution {
public:
    int deleteAndEarn(vector<int>& nums) {
        int max_val = *max_element(nums.begin(),nums.end());
        vector<int> house(max_val+1,0);
        for(int i=0;i<nums.size();i++) house[nums[i]] += nums[i];

        //打家劫舍
        int p=0,q=house[0];int m = q;
        for(int i=1;i<house.size();i++){
            m = max(p+house[i],q);
            p = q;
            q = m;
        }
        return m;
    }
};