【每日一题】3186. 施咒的最大总伤害
一.题目描述
3186. 施咒的最大总伤害
一个魔法师有许多不同的咒语。
给你一个数组 power ,其中每个元素表示一个咒语的伤害值,可能会有多个咒语有相同的伤害值。
已知魔法师使用伤害值为 power[i] 的咒语时,他们就 不能 使用伤害为 power[i] - 2 ,power[i] - 1 ,power[i] + 1 或者 power[i] + 2 的咒语。
每个咒语最多只能被使用 一次 。
请你返回这个魔法师可以达到的伤害值之和的 最大值 。
实例1:
输入:power = [1,1,3,4]
输出:6
解释:
可以使用咒语 0,1,3,伤害值分别为 1,1,4,总伤害值为 6 。
实例2:
输入:power = [7,1,6,6]
输出:13
解释:
可以使用咒语 1,2,3,伤害值分别为 1,6,6,总伤害值为 13 。
提示:
- 1 <= power.length <= 105
- 1 <= power[i] <= 109
二. 解析
这是一个“值域打家劫舍”问题:在按伤害值排序后的唯一值上做动态规划,选择某个值时必须跳过与它差距小于 3 的值。
- 首选合并计数,统计每个伤害出现的次数,使用hash表进行统计。
- 然后,对伤害进行排序统计,便于后续对条件进行处理
- 进行动态规划
2.1 动态规划
- 状态表示:dp[i]:[0,i]区间内的子序列中,总数最大的值
- 状态转移方程
以最后一个位置i讨论
不选i,就是dp[i-1]
选i,就要找到i的左边满足nums[i]-3>=nums[j]的值
dp[j]+nums[i]*次数 - 初始化
dp[0]=nums[0]*次数 - 填表顺序
从左到右 - 返回值
dp[n-1]
注意细节:j从-1开始表示没有合法前驱,所以选i时如果j<0,那么dp[j]就不存在。
在寻找第一个符合的位置时,判断条件是j+1<i && nums[j+1]<=nums[i]-3
而不是nums[j]<=nums[i]-3,因为下面的判断会让j出现在第一个不符合的位置上。
三.代码
class Solution
{
public:long long maximumTotalDamage(vector<int>& power) {//1.hash统计计数unordered_map<int,long long> hash;for(auto x:power)hash[x]++;vector<int> nums;//2.对不同的伤害进行排序nums.reserve(hash.size());for(auto& [a,b]:hash)nums.push_back(a);sort(nums.begin(),nums.end());//3.动态规划int n=nums.size();vector<long long> dp(n);dp[0]=nums[0]*hash[nums[0]];int j=-1;for(int i=1;i<n;i++){//找到j的最大且合理的位置while(j+1<i && nums[j+1]<=nums[i]-3)j++;long long tmp=(j<0 ? 0 : dp[j])+nums[i]*hash[nums[i]];dp[i]=max(dp[i-1],tmp);}return dp[n-1];}
};