《小C大作战得分计算》 | 豆包MarsCode AI刷题

224 阅读6分钟

问题理解

在游戏“小C大作战”中,每个角色都有一个能量值 a[i],能量大的角色可以击败能量小的角色,并获得对应的分数 b[i]。每个角色被击败后,击败者可以获得 b[i] 得分。为了维持游戏平衡,每个角色最多只能击败 m 个其他角色。需要计算出每个角色最终可以获得的最大分数。

保证这些球的能量值两两不同。

数据结构选择

  • 列表(List)

    • 存储角色的能量值 a 和分数 b
    • 存储结果。
  • 堆(Heap)

    • 使用最小堆维护当前击败的 m 个得分最高的角色,便于快速获取最小的 b[i] 来进行替换。
  • 元组(Tuple)

    • 存储角色的能量值、分数及原始索引,便于排序和结果映射。

算法步骤

为了高效地计算每个角色可以获得的最大分数,采用以下步骤:

  1. 准备数据

    • 将角色的信息组合成包含能量值、分数和原始索引的元组列表。
    • 按照能量值升序排序,保证在处理高能量值角色时,已经处理并维护了所有可以被击败的低能量值角色。
  2. 使用最小堆维护得分

    • 初始化一个最小堆,用于存储当前击败的 m 个角色的得分。
    • 维护一个变量 sum_b 来记录当前堆中所有得分的总和。
  3. 遍历排序后的角色列表

    • 对于每个角色:
      • sum_b 赋值给该角色的得分(因为这代表了击败当前 m 个最高得分的角色)。
      • 将当前角色的得分 b[i] 添加到堆中,并更新 sum_b
      • 如果堆的大小超过 m,则弹出堆顶(最小的 b[i]),并从 sum_b 中减去相应的得分。
  4. 映射结果

    • 根据原始索引,将计算得到的得分赋值到结果列表的相应位置。

代码实现

import heapq

def solution(n: int, m: int, a: list, b: list) -> list:
    # 组合角色信息:(能量值, 分数, 原始索引)
    roles = [(a[i], b[i], i) for i in range(n)]
    # 按能量值升序排序
    roles.sort(key=lambda x: x[0])
    
    min_heap = []
    sum_b = 0
    result = [0] * n
    
    for energy, score, idx in roles:
        # 当前角色的最大分数
        result[idx] = sum_b
        # 将当前角色的分数加入堆
        heapq.heappush(min_heap, score)
        sum_b += score
        # 如果堆的大小超过m,移除最小的分数
        if len(min_heap) > m:
            removed = heapq.heappop(min_heap)
            sum_b -= removed
    return result

代码解释

  1. 组合并排序角色信息

    roles = [(a[i], b[i], i) for i in range(n)]
    roles.sort(key=lambda x: x[0])
    
    • 将每个角色的能量值、分数和原始索引组合成元组。
    • 按照能量值升序排序,确保在处理高能量值角色时,低能量值角色的信息已经被考虑。
  2. 初始化最小堆和结果列表

    min_heap = []
    sum_b = 0
    result = [0] * n
    
    • min_heap 用于存储当前击败的角色的得分,保持堆的大小不超过 m
    • sum_b 记录堆中所有得分的总和,便于快速赋值给当前角色。
    • result 列表用于存储每个角色的最终得分。
  3. 遍历角色并计算得分

    for energy, score, idx in roles:
        # 当前角色的最大分数
        result[idx] = sum_b
        
        # 将当前角色的分数加入堆
        heapq.heappush(min_heap, score)
        sum_b += score
        
        # 如果堆的大小超过m,移除最小的分数
        if len(min_heap) > m:
            removed = heapq.heappop(min_heap)
            sum_b -= removed
    
    • 对于每个角色,sum_b 已经代表了所有能被当前角色击败的 m 个最高得分的角色的总和。
    • 将当前角色的得分加入堆中,以便后续角色可以选择击败它。
    • 如果堆的大小超过 m,则弹出最小的得分,保持堆的大小为 m,并相应更新 sum_b
  4. 返回结果

    return result
    
    • 最终,result 列表包含了每个角色可以获得的最大分数,按照原始输入顺序排列。

测试用例解释

  1. 测试用例 1

    print(solution(5, 3, [1, 3, 5, 2, 4], [1, 2, 3, 4, 5]) == [0, 5, 11, 1, 7])
    
    • 过程
      • 排序后的角色信息:
        [(1, 1, 0), (2, 4, 3), (3, 2, 1), (4, 5, 4), (5, 3, 2)]
        
      • 逐步处理:
        1. 角色0(能量1):sum_b = 0,得分 0
          • 加入堆:[1],sum_b = 1
        2. 角色3(能量2):sum_b = 1,得分 1
          • 加入堆:[1, 4],sum_b = 5
        3. 角色1(能量3):sum_b = 5,得分 5
          • 加入堆:[1, 4, 2],sum_b = 7
        4. 角色4(能量4):sum_b = 7,得分 7
          • 加入堆:[1, 4, 2, 5]sum_b = 12`
          • 弹出最小得分 1sum_b = 11
        5. 角色2(能量5):sum_b = 11,得分 11
          • 加入堆:[2, 4, 5, 3]sum_b = 14`
          • 弹出最小得分 2sum_b = 12
      • 最终结果按照原始索引:[0, 5, 11, 1, 7]
  2. 测试用例 2

    print(solution(4, 2, [10, 20, 30, 40], [2, 4, 6, 8]) == [0, 2, 6, 10])
    
    • 过程
      • 排序后的角色信息:
        [(10, 2, 0), (20, 4, 1), (30, 6, 2), (40, 8, 3)]
        
      • 逐步处理:
        1. 角色0(能量10):sum_b = 0,得分 0
          • 加入堆:[2],sum_b = 2
        2. 角色1(能量20):sum_b = 2,得分 2
          • 加入堆:[2, 4],sum_b = 6
        3. 角色2(能量30):sum_b = 6,得分 6
          • 加入堆:[2, 4, 6]sum_b = 12`
          • 弹出最小得分 2sum_b = 10
        4. 角色3(能量40):sum_b = 10,得分 10
          • 加入堆:[4, 6, 8]sum_b = 18`
          • 弹出最小得分 4sum_b = 14
      • 最终结果按照原始索引:[0, 2, 6, 10]
  3. 测试用例 3

    print(solution(6, 1, [6, 12, 18, 24, 30, 36], [5, 10, 15, 20, 25, 30]) == [0, 5, 10, 15, 20, 25])
    
    • 过程
      • 排序后的角色信息:
        [(6, 5, 0), (12, 10, 1), (18, 15, 2), (24, 20, 3), (30, 25, 4), (36, 30, 5)]
        
      • 逐步处理:
        1. 角色0(能量6):sum_b = 0,得分 0
          • 加入堆:[5],sum_b = 5
        2. 角色1(能量12):sum_b = 5,得分 5
          • 加入堆:[5, 10]sum_b = 15`
          • 弹出最小得分 5sum_b = 10
        3. 角色2(能量18):sum_b = 10,得分 10
          • 加入堆:[10, 15]sum_b = 25`
          • 弹出最小得分 10sum_b = 15
        4. 角色3(能量24):sum_b = 15,得分 15
          • 加入堆:[15, 20]sum_b = 35`
          • 弹出最小得分 15sum_b = 20
        5. 角色4(能量30):sum_b = 20,得分 20
          • 加入堆:[20, 25]sum_b = 45`
          • 弹出最小得分 20sum_b = 25
        6. 角色5(能量36):sum_b = 25,得分 25
          • 加入堆:[25, 30]sum_b = 55`
          • 弹出最小得分 25sum_b = 30
      • 最终结果按照原始索引:[0, 5, 10, 15, 20, 25]

时间和空间复杂度

  • 时间复杂度

    • 排序步骤:O(nlogn)
    • 遍历和堆操作:每次 heapq.heappushheapq.heappop 操作的时间复杂度为 O(logm),遍历 n 次,总时间复杂度为 O(nlogm)
    • 总体时间复杂度为 O(nlogn + nlogm)
  • 空间复杂度

    • 组合后的 members 列表:O(n)
    • 最小堆:O(m)
    • 结果列表:O(n)
    • 总体空间复杂度为 O(n + m)