施咒的最大总伤害
题目大意
给你一个数组 power ,其中每个元素表示一个咒语的伤害值,可能会有多个咒语有相同的伤害值。
已知魔法师使用伤害值为 power[i] 的咒语时,他们就 不能 使用伤害为 power[i] - 2 ,power[i] - 1 ,power[i] + 1 或者 power[i] + 2 的咒语。
每个咒语最多只能被使用 一次 。
返回可以达到的伤害值之和的 最大值 。
数据范围
- 1 <= power.length <= 105
 - 1 <= power[i] <= 109
 
思路
与 打家劫舍 相似
- 
选择咒语与出现次序无关,只与值有关;并且一个咒语有多个可能值,是每个咒语最多只用一次,而 不是每个值的咒语最多只能用一次 , 所以可能将所有值进行统计,还有它们的出现次数,这样 值*出现次数=伤害值; 再将统计出来的进行排序, 因为选择值为x的就不能选x-1和x-2
 - 
从后往前推(dfs),最后一个是i
- 选i ,那么i - 1 和 i - 2 都不能选,只能另外选择最大的小于i - 2的数
- 难点在于如果像之前一样开个 vector
f(ma + 1)的数组,或者使用a, b, c, d依次更新,由于数据过大,会导致爆内存  - 就要使用哈希表,以及用while循环来寻找 最大的小于i-2的数,并且在power中出现了
 
 - 难点在于如果像之前一样开个 vector
 - 不选i,那么就选i前面的一个数即可。这里的前面的一个数,并不是\(i-1\),而是在power中的数排序之后,排在i前面的一个数
 
 - 选i ,那么i - 1 和 i - 2 都不能选,只能另外选择最大的小于i - 2的数
 - 
从前往后推(递推),当前是i
- 
选i,再选择<i - 2最大的数,也是用while循环
 - 
不选i,直接选择i的前一个数
 - 
比较这两种情况谁更大
 
 - 
 
代码
爆内存的普通做法
class Solution {
public:
    using ll = long long;
    long long maximumTotalDamage(vector<int>& power) {
        int ma = ranges::max(power);
        int n = power.size(), cnt = 0;
        vector<int> a(ma + 1);
        for(auto& u : power) {
            if(a[u] == 0) cnt++;
            a[u] += u;
        }
        vector<ll> f(ma + 1);
        if(cnt == 1) return a[ma];
        f[1] = a[1], f[2] = max(a[1], a[2]);
        for(int i = 3; i <= ma; i++) {
            f[i] = max(f[i - 1], f[i - 2]);
            f[i] = max(f[i], f[i - 3] + a[i]);
        }
        return f[ma];
    }
};
递归版改进做法
class Solution {
public:
    using ll = long long;
    long long maximumTotalDamage(vector<int>& power) {
        unordered_map<ll, ll> mp;
        for(auto& u : power) mp[u]++;
        vector<pair<ll, ll>> a(mp.begin(), mp.end());
        ranges::sort(a);
        ll n = a.size();
        vector<ll> memo(n, -1);
        auto dfs = [&](auto&& dfs, ll x) -> ll {
            if(x < 0) return 0;
            ll& res = memo[x];
            if(res != -1) return res;
            ll j = x;
            auto& [u, v] = a[x];
            while(j && a[j - 1].first >= a[x].first - 2) j--;
            return res = max(dfs(dfs, x - 1), dfs(dfs, j - 1) + (ll)u * v);
        };
        return dfs(dfs, n - 1);
    }
};
递推版改进做法
class Solution {
public:
    using ll = long long;
    long long maximumTotalDamage(vector<int>& power) {
        unordered_map<ll, ll> mp;
        for(auto& u : power) mp[u] ++;
        vector<pair<ll, ll>> a(mp.begin(), mp.end());
        ranges::sort(a); //[出现的数, 出现次数]
        ll n = a.size();
        vector<ll> f(n); //第几小的值的出现次数
        f[0] = (ll)a[0].first * a[0].second; //最小的值*出现次数
        for(int i = 1, j = 0; i < n; i++) {
            auto& [u, v] = a[i];
            while(u - a[j].first > 2) j++; //直到u - a[j].first <= 2  !!!那么u - a[j - 1].first肯定会 < 2  !符合
            if(j == 0) f[i] = max(f[i - 1], (ll)u * v);  //j不能减少了
            else f[i] = max(f[i - 1], f[j - 1] + (ll)u * v);
        }
        return f[n - 1];
    }
};
                    
                
                
            
        
浙公网安备 33010602011771号