问题理解
在游戏“小C大作战”中,每个角色都有一个能量值 a[i],能量大的角色可以击败能量小的角色,并获得对应的分数 b[i]。每个角色被击败后,击败者可以获得 b[i] 得分。为了维持游戏平衡,每个角色最多只能击败 m 个其他角色。需要计算出每个角色最终可以获得的最大分数。
保证这些球的能量值两两不同。
数据结构选择
-
列表(List):
- 存储角色的能量值
a和分数b。 - 存储结果。
- 存储角色的能量值
-
堆(Heap):
- 使用最小堆维护当前击败的
m个得分最高的角色,便于快速获取最小的b[i]来进行替换。
- 使用最小堆维护当前击败的
-
元组(Tuple):
- 存储角色的能量值、分数及原始索引,便于排序和结果映射。
算法步骤
为了高效地计算每个角色可以获得的最大分数,采用以下步骤:
-
准备数据:
- 将角色的信息组合成包含能量值、分数和原始索引的元组列表。
- 按照能量值升序排序,保证在处理高能量值角色时,已经处理并维护了所有可以被击败的低能量值角色。
-
使用最小堆维护得分:
- 初始化一个最小堆,用于存储当前击败的
m个角色的得分。 - 维护一个变量
sum_b来记录当前堆中所有得分的总和。
- 初始化一个最小堆,用于存储当前击败的
-
遍历排序后的角色列表:
- 对于每个角色:
- 将
sum_b赋值给该角色的得分(因为这代表了击败当前m个最高得分的角色)。 - 将当前角色的得分
b[i]添加到堆中,并更新sum_b。 - 如果堆的大小超过
m,则弹出堆顶(最小的b[i]),并从sum_b中减去相应的得分。
- 将
- 对于每个角色:
-
映射结果:
- 根据原始索引,将计算得到的得分赋值到结果列表的相应位置。
代码实现
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
代码解释
-
组合并排序角色信息:
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] * nmin_heap用于存储当前击败的角色的得分,保持堆的大小不超过m。sum_b记录堆中所有得分的总和,便于快速赋值给当前角色。result列表用于存储每个角色的最终得分。
-
遍历角色并计算得分:
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。
- 对于每个角色,
-
返回结果:
return result- 最终,
result列表包含了每个角色可以获得的最大分数,按照原始输入顺序排列。
- 最终,
测试用例解释
-
测试用例 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)] - 逐步处理:
- 角色0(能量1):
sum_b = 0,得分0- 加入堆:[1],
sum_b = 1
- 加入堆:[1],
- 角色3(能量2):
sum_b = 1,得分1- 加入堆:[1, 4],
sum_b = 5
- 加入堆:[1, 4],
- 角色1(能量3):
sum_b = 5,得分5- 加入堆:[1, 4, 2],
sum_b = 7
- 加入堆:[1, 4, 2],
- 角色4(能量4):
sum_b = 7,得分7- 加入堆:[1, 4, 2, 5]
,sum_b = 12` - 弹出最小得分
1,sum_b = 11
- 加入堆:[1, 4, 2, 5]
- 角色2(能量5):
sum_b = 11,得分11- 加入堆:[2, 4, 5, 3]
,sum_b = 14` - 弹出最小得分
2,sum_b = 12
- 加入堆:[2, 4, 5, 3]
- 角色0(能量1):
- 最终结果按照原始索引:[0, 5, 11, 1, 7]
- 排序后的角色信息:
- 过程:
-
测试用例 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)] - 逐步处理:
- 角色0(能量10):
sum_b = 0,得分0- 加入堆:[2],
sum_b = 2
- 加入堆:[2],
- 角色1(能量20):
sum_b = 2,得分2- 加入堆:[2, 4],
sum_b = 6
- 加入堆:[2, 4],
- 角色2(能量30):
sum_b = 6,得分6- 加入堆:[2, 4, 6]
,sum_b = 12` - 弹出最小得分
2,sum_b = 10
- 加入堆:[2, 4, 6]
- 角色3(能量40):
sum_b = 10,得分10- 加入堆:[4, 6, 8]
,sum_b = 18` - 弹出最小得分
4,sum_b = 14
- 加入堆:[4, 6, 8]
- 角色0(能量10):
- 最终结果按照原始索引:[0, 2, 6, 10]
- 排序后的角色信息:
- 过程:
-
测试用例 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)] - 逐步处理:
- 角色0(能量6):
sum_b = 0,得分0- 加入堆:[5],
sum_b = 5
- 加入堆:[5],
- 角色1(能量12):
sum_b = 5,得分5- 加入堆:[5, 10]
,sum_b = 15` - 弹出最小得分
5,sum_b = 10
- 加入堆:[5, 10]
- 角色2(能量18):
sum_b = 10,得分10- 加入堆:[10, 15]
,sum_b = 25` - 弹出最小得分
10,sum_b = 15
- 加入堆:[10, 15]
- 角色3(能量24):
sum_b = 15,得分15- 加入堆:[15, 20]
,sum_b = 35` - 弹出最小得分
15,sum_b = 20
- 加入堆:[15, 20]
- 角色4(能量30):
sum_b = 20,得分20- 加入堆:[20, 25]
,sum_b = 45` - 弹出最小得分
20,sum_b = 25
- 加入堆:[20, 25]
- 角色5(能量36):
sum_b = 25,得分25- 加入堆:[25, 30]
,sum_b = 55` - 弹出最小得分
25,sum_b = 30
- 加入堆:[25, 30]
- 角色0(能量6):
- 最终结果按照原始索引:[0, 5, 10, 15, 20, 25]
- 排序后的角色信息:
- 过程:
时间和空间复杂度
-
时间复杂度:
- 排序步骤:
O(nlogn) - 遍历和堆操作:每次
heapq.heappush和heapq.heappop操作的时间复杂度为O(logm),遍历n次,总时间复杂度为O(nlogm) - 总体时间复杂度为
O(nlogn + nlogm)
- 排序步骤:
-
空间复杂度:
- 组合后的
members列表:O(n) - 最小堆:
O(m) - 结果列表:
O(n) - 总体空间复杂度为
O(n + m)
- 组合后的