小M的得分挑战 | 豆包MarsCode AI刷题

121 阅读3分钟

一、问题描述

小M有一个长度为 n 的数组 a,初始分数为 0。小M每次可以选择两个整数,并且这两个数的差值不能超过给定的 k。每次选择时,小M会获得这两个数的乘积作为分数,并且已经被选择过的数不能再被选择。任务是帮助小M计算,最多能获得多少分数。

二、问题分析

  1. 选择条件

    • 每次选择两个数,要求它们的差值不超过 k。若差值超过 k,则不能再选择这对数。
  2. 目标

    • 我们的目标是通过多次选择,计算出小M可以获得的最大分数。所以选择的策略是每次选取两个差值不超过k且乘积最大的数。
  3. 边界条件

    • 如果没有符合差值条件的数对,返回分数 0

三、解题思路

  1. 排序优化
    • 为了便于选择,首先将数组 a 排序。排序后的数组中,相邻的元素差值最小,因此可以更容易地满足差值不超过 k 的条件。
  2. 贪心选择
    • 在排序后的数组中,从大到小选取两个数。贪心地选择乘积最大的数对,能够获得较大的分数。
  3. 策略
    • 若两个数满足差值不超过 k,则选择它们的乘积,累加到分数中。
    • 如果不满足条件,则跳过当前数,继续选择下一个数。
  4. 结束条件
    • 当数组中的数都已遍历,或者无法找到满足条件的数对时,结束计算。

四、代码实现

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

五、复杂度分析

  1. 时间复杂度

    • 排序操作的时间复杂度是 O(n log n),其中 n 是数组的长度。
    • while 循环最多进行 n 次,每次迭代操作常数时间,因此循环部分的时间复杂度为 O(n)
    • 总的时间复杂度为 O(n log n),因为排序是主要的时间开销。
  2. 空间复杂度

    • 使用了常数级别的额外空间,空间复杂度为 O(1)。排序操作是原地进行的,因此不需要额外的存储空间。

六、总结与收获

通过这道题,我学会了如何在满足特定条件的情况下进行贪心选择,尤其是如何高效地找到满足条件的数对并计算它们的乘积。在解决这类问题时,排序是一个常见的技巧,可以帮助我们简化计算过程并快速找到候选数对。