一、问题描述
小M有一个长度为 n 的数组 a,初始分数为 0。小M每次可以选择两个整数,并且这两个数的差值不能超过给定的 k。每次选择时,小M会获得这两个数的乘积作为分数,并且已经被选择过的数不能再被选择。任务是帮助小M计算,最多能获得多少分数。
二、问题分析
-
选择条件:
- 每次选择两个数,要求它们的差值不超过
k。若差值超过k,则不能再选择这对数。
- 每次选择两个数,要求它们的差值不超过
-
目标:
- 我们的目标是通过多次选择,计算出小M可以获得的最大分数。所以选择的策略是每次选取两个差值不超过k且乘积最大的数。
-
边界条件:
- 如果没有符合差值条件的数对,返回分数
0。
- 如果没有符合差值条件的数对,返回分数
三、解题思路
- 排序优化:
- 为了便于选择,首先将数组
a排序。排序后的数组中,相邻的元素差值最小,因此可以更容易地满足差值不超过k的条件。
- 为了便于选择,首先将数组
- 贪心选择:
- 在排序后的数组中,从大到小选取两个数。贪心地选择乘积最大的数对,能够获得较大的分数。
- 策略:
- 若两个数满足差值不超过
k,则选择它们的乘积,累加到分数中。 - 如果不满足条件,则跳过当前数,继续选择下一个数。
- 若两个数满足差值不超过
- 结束条件:
- 当数组中的数都已遍历,或者无法找到满足条件的数对时,结束计算。
四、代码实现
def solution(n: int, k: int, a: list) -> int:
# 将数组降序排列,以便于贪心选择
a.sort(reverse=True)
i = 0
sum = 0 # 初始化总分数
while i < n - 1: # 防止越界
# 如果当前元素和下一个元素的差值不超过 k,选择它们并累加乘积
if a[i] - a[i + 1] <= k:
sum += a[i] * a[i + 1]
i += 2 # 选择两个数后,跳过这两个数
else:
i += 1 # 如果差值不满足条件,跳过当前元素,继续判断下一个元素
return sum
# 测试用例
if __name__ == '__main__':
print(solution(6, 2, [1, 1, 4, 5, 1, 4]) == 21) # 输出 21
print(solution(4, 1, [3, 3, 4, 4]) == 25) # 输出 25
print(solution(5, 0, [2, 2, 2, 2, 2]) == 8) # 输出 8
五、复杂度分析
-
时间复杂度:
- 排序操作的时间复杂度是
O(n log n),其中n是数组的长度。 while循环最多进行n次,每次迭代操作常数时间,因此循环部分的时间复杂度为O(n)。- 总的时间复杂度为
O(n log n),因为排序是主要的时间开销。
- 排序操作的时间复杂度是
-
空间复杂度:
- 使用了常数级别的额外空间,空间复杂度为
O(1)。排序操作是原地进行的,因此不需要额外的存储空间。
- 使用了常数级别的额外空间,空间复杂度为
六、总结与收获
通过这道题,我学会了如何在满足特定条件的情况下进行贪心选择,尤其是如何高效地找到满足条件的数对并计算它们的乘积。在解决这类问题时,排序是一个常见的技巧,可以帮助我们简化计算过程并快速找到候选数对。