当前位置: 首页 > news >正文

LeetCode每日一题,20251011

施咒的最大伤害

1. 问题描述

给定一个魔法师拥有多个伤害值不同的咒语,目标是最大化魔法师的伤害值总和,但有如下约束:

  • 如果选择了伤害为 power[i] 的咒语,则无法选择伤害为 power[i] - 2power[i] - 1power[i] + 1power[i] + 2 的咒语。
  • 每个伤害值最多只能使用一次。

在这个问题中,您需要返回魔法师能使用的最大伤害值总和。


2. 解决思路

为了求解最大伤害值总和,我们可以使用动态规划(DP)结合二分查找来优化。

动态规划(DP)

我们用一个数组 f[i] 表示考虑前 i 个不同的伤害值时能得到的最大总伤害值。
对于每个伤害值 a[i],我们有两个选择:

  1. 跳过当前伤害值 a[i]:此时的最大伤害为 f[i-1]
  2. 选择当前伤害值 a[i]:此时我们需要考虑前面已经选择的伤害值,前提是前面的伤害值不与当前的伤害值冲突。
二分查找

为了高效地找到前一个不冲突的伤害值,我们使用二分查找:

  • 对于当前伤害值 x = a[i],我们需要找出 最后一个小于 x-2 的伤害值下标。
  • 使用二分查找可以在对已排序的伤害值数组 a 进行查找时,节省时间复杂度。
注意点
  • 边界问题:在二分查找时,查找的位置是从 0i-1,但动态规划的数组 f[i] 是从 1 开始的。为了处理这个偏移,我们在 f 数组中的下标加 1
  • 在二分查找时,如果找不到符合条件的伤害值,应该将 l 设置为 -1,表示没有找到前一个兼容的伤害值。

3. 实现代码
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;class Solution {public long maximumTotalDamage(int[] power) {// 1️⃣ 统计每种伤害值的出现次数Map<Integer, Integer> cnt = new HashMap<>();for (int x : power) {cnt.merge(x, 1, Integer::sum); // 计数}// 2️⃣ 将伤害值提取出来并排序int n = cnt.size();int[] a = new int[n];int k = 0;for (int x : cnt.keySet()) {a[k++] = x;}Arrays.sort(a); // 将不同伤害值升序排列// 3️⃣ 动态规划数组,f[i] 表示考虑前 i 个不同伤害值的最大伤害值long[] f = new long[n + 1]; // 使用 f 数组来存储最大伤害值// 4️⃣ 动态规划:遍历每个伤害值并进行二分查找for (int i = 1; i <= n; i++) {int x = a[i-1];// 用二分查找找到最后一个小于 x-2 的位置int l = 0, r = i - 1;while (l < r) {int mid = (l + r + 1) >> 1; // 二分查找if (a[mid] < x - 2) l = mid; // 如果 a[mid] < x-2,继续向右查找else r = mid - 1;           // 否则,缩小查找范围}// 边界处理:如果没有找到合适的位置,l 会被更新为 -1if (a[l] >= x - 2) l = -1;// 选择当前伤害值或跳过当前伤害值long take = f[l+1] + (long) x * cnt.get(x); // 选择当前伤害值long skip = f[i-1];                         // 不选择当前伤害值f[i] = Math.max(skip, take);                // 取最大值}// 5️⃣ 返回最大伤害值return f[n];}// 测试public static void main(String[] args) {int[] power = {1, 1, 3, 4};Solution sol = new Solution();System.out.println(sol.maximumTotalDamage(power)); // 输出 10}
}

4. 核心逻辑讲解
  1. 统计伤害值的出现次数
    我们通过 Map<Integer, Integer> 来记录每个伤害值出现的次数,确保在之后的计算中,我们能够正确地处理相同伤害值的咒语。

  2. 排序
    将所有不同的伤害值排序,以便于后续的二分查找。

  3. 二分查找
    对于当前伤害值 x = a[i],我们需要找到前一个不与它冲突的伤害值。在二分查找中,我们搜索 最后一个小于 x-2 的下标。

    • 如果找到,说明前面的值可以与当前值共存;
    • 如果找不到,说明当前值没有任何兼容的前驱伤害值。
  4. 动态规划状态转移

    • take:选择当前伤害值时,最大伤害为 f[l+1] + (long) x * cnt.get(x),其中 l+1 表示前 l 组的最优解。
    • skip:跳过当前伤害值时,最大伤害为 f[i-1]
  5. 边界处理

    • 在二分查找后,若没有找到适合的前驱位置,l 会被更新为 -1,因此我们通过 f[l+1] 来保证边界不出错。

5. 时间复杂度分析
  1. 统计伤害值出现次数
    这一步的时间复杂度是 O(n),其中 npower 数组的长度。

  2. 排序
    将伤害值排序的时间复杂度是 O(n log n),其中 n 是不同伤害值的数量。

  3. 动态规划与二分查找
    对每个伤害值执行一次二分查找,二分查找的时间复杂度是 O(log n),因此整体的时间复杂度为 O(n log n)。

因此,整个算法的时间复杂度为 O(n log n),这是一个高效的解决方案。


http://www.dtcms.com/a/470045.html

相关文章:

  • Linux c 在内存中创建zip,最后写入测试
  • 做品牌推广用什么网站眉山网站开发
  • 静态网站源码下载东营免费网站制作
  • 智能体架构设计
  • 2025年三个月自学手册 网络安全(黑客技术),新手小白看这一篇就够了!
  • 无锡做网站要多少钱织梦网站栏目不能更新
  • 网站设计制作公司需要什么资质西部数码网站管理助手 破解版
  • 物联网卡的TAC
  • 学习物联网可以做什么工作?
  • [嵌入式系统-100]:IoT(物联网)与AIoT(人工智能物联网)
  • 网站目录链接怎么做巩义服务专业网站建设
  • 查看apk应用包名
  • 代做淘宝联盟网站东莞网站建设排名公司
  • React 在使用antd的RangePicker时,解决季度选择不是按照当前季度的第一天显示问题
  • 如何理解Vue是渐进式框架
  • 【Vue】——Pinia
  • 网站开发合同注意滨州做网站建设
  • Linux系统编程01:进程概念(万字图文解析)
  • 前端通用AI rules定义,适用于Cursor ,Trae,Qorder等AI开发工具
  • Go 协程在实际项目中的应用详解
  • 最简单的做网站南沙滩网站建设
  • Hive 知识点梳理
  • MySQL常见报错分析及解决方案总结(15)---Can’t connect to MySQL server on ‘localhost‘ (10061)
  • 网站上做的vi设计是怎么做的互联网设计公司排名
  • jetson orin nane 编译 paddle
  • 兰州网站卡法百度网页收录
  • [1-02-05].第04章:Win工具
  • 软件需求规格说明书(SRS)标准模板与编写指南——含功能需求、非功能需求、接口设计与验收标准
  • VS 2022 中创建一个最小的 Django 项目
  • 建设网站的功能定位是什么原因网站建设模版