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

119 阅读5分钟

小M的得分挑战 - MarsCode

问题描述

小M有一个长度为 n 的数组 a,初始分数为 0

小M每次可以选择两个整数,并且这两个数的差值不能超过 k。小M会获得这两个数的乘积作为分数,并且已经被选择过的数不能再被选择。

你需要帮助小M计算,最多能获得多少分数?


测试样例

样例1:

输入:n = 6, k = 2, a = [1, 1, 4, 5, 1, 4]
输出:21

样例2:

输入:n = 4, k = 1, a = [3, 3, 4, 4]
输出:25

样例3:

输入:n = 5, k = 0, a = [2, 2, 2, 2, 2]
输出:8

题目分析

这个问题可以看作是一个贪心问题或者匹配问题。我们需要选择符合条件(差值不超过 k)的数对,以最大化分数。为了更好地理解和解决问题,我们可以进行以下分析:

  1. 选择数对的条件:每次选择两个数 xy,要求它们的差值 |x - y| <= k。这意味着,数对之间不能有太大的差异。因此,首先考虑将数组进行排序,便于后续匹配。
  2. 目标是最大化分数:每次选择两个数 xy,得到的分数是它们的乘积 x * y。因此,我们希望选择的数对中的数值尽可能大,以最大化乘积。
  3. 贪心策略:考虑贪心的选择策略,首先将数组排序,然后尽可能选择较大的数对。我们可以从数组的两端开始尝试选择满足条件的数对。

解题思路

  1. 排序数组:为了便于后续操作,我们可以先对数组 a 进行排序。排序后,对于每对相邻的数,它们的差值应该较小,因此可以尝试选择这些数作为一对。
  2. 双指针法:我们可以使用双指针的策略,指针 i 从左向右扫描数组,指针 j 从右向左扫描数组,尝试找出符合条件的数对。每当我们找到一个符合条件的数对,我们就将它们的乘积加到总分数中,并且两个指针分别向内移动,继续尝试新的匹配。
  3. 考虑选择后的数组更新:一旦选择了一个数对,这两个数就不能再被使用。因此,实际上我们可以通过标记这些数是否已经被使用,来确保每个数只能参与一次配对。
  4. 结束条件:当无法再找到符合条件的数对时,算法终止。

具体步骤

  1. 排序数组:将数组 a 排序,方便找到数对。
  2. 使用双指针:初始化指针 i 指向数组的左端,指针 j 指向数组的右端。如果 a[j] - a[i] <= k,则选择这两个数作为一个有效的数对,计算它们的乘积并累加到总分数,然后移动指针 ij 向内。
  3. 继续寻找:如果 a[j] - a[i] > k,则说明这两个数不能作为一对,指针 j 向内移动,减少较大的数,重新尝试。
  4. 结束:当指针 i 超过指针 j 时,说明数组中的数已经无法继续配对,结束算法。
def solution(n, k, a):
    # 排序数组a
    a.sort()
    
    # 用来存储总得分
    total_score = 0
    
    # 遍历数组,尝试配对
    i = n - 1
    while i > 0:
        if a[i] - a[i - 1] <= k:
            # 如果当前数和前一个数的差值小于等于k,则可以配对
            total_score += a[i] * a[i - 1]
            i -= 2  # 配对后跳过这两个数
        else:
            i -= 1  # 当前数不能和前一个数配对,继续尝试下一个数
    
    return total_score

# 测试样例
print(solution(6, 2, [1, 1, 4, 5, 1, 4]))  # 输出:21
print(solution(4, 1, [3, 3, 4, 4]))        # 输出:25
print(solution(5, 0, [2, 2, 2, 2, 2]))     # 输出:8

代码流程解释

  • 排序:首先对数组 a 进行排序。排序后,相邻的元素的差值会更小,这样我们可以更容易地判断它们是否符合配对条件。
  • 贪心策略:通过倒序遍历数组,并从右侧逐步进行配对。每次遇到一对差值符合条件的数,就将它们的乘积累加到总得分,并跳过这两个数。如果差值不符合条件,则继续向左移一位,直到所有元素都处理完。
  • 配对策略:这段代码的配对策略是从数组的末尾开始(即选择较大的数),然后逐步向左配对,这样可以尽量保证较大的数被用于计算乘积,从而更可能获得较大的分数。

感想

这段代码通过排序和贪心策略解决了一个配对问题,其核心思路是将数组排序后,尽可能地选择符合条件的数对,以最大化乘积。使用从右至左的双指针方式,不仅能有效减少不必要的判断,还能够确保较大的数对优先被选择,从而最大化得分。

我觉得这段代码实现较为简洁高效,主要采用了贪心算法的思想:每次选择当前最优的数对(差值小于等于 k),从而累加最大分数。通过排序将问题转换成更易于处理的形式,确保算法在处理较大数组时依然高效。

不过,这段代码的贪心策略也有局限性。如果题目要求更复杂的配对策略,可能需要更精细的动态规划或图论方法。总体而言,对于当前问题,排序+贪心的策略是非常合适的,能够在合理的时间复杂度内得到正确答案。