环形数组中的最大贡献值 | MarsCode AI刷题

6 阅读4分钟

问题描述

小S拿到了一个长度为 nn 的环形数组,并定义了两个下标 ii 和 jj 的贡献值公式为:
f(i, j) = (a_i + a_j) × dist(i, j)
其中 dist(i, j) 是下标 ii 和 jj 在数组中的最短距离。小S希望找到一对下标,使得它们的贡献值尽可能大。环形数组的特点是最左和最右的元素也是相邻的。你需要帮助她找到最大贡献值。

例如,给定数组 [1, 2, 3],由于是环形数组,任意两个下标的距离都是1,因此 f(2,3)=(2+3)×1=5f(2,3)=(2+3)×1=5。


测试样例

样例1:

输入:n = 3,a = [1, 2, 3]
输出:5

样例2:

输入:n = 4,a = [4, 1, 2, 3]
输出:12

样例3:

输入:n = 5,a = [1, 5, 3, 7, 2]
输出:24

思路过程: 环形数组中两个下标之间的最短距离是如何计算的。在环形数组中,如果数组长度为 n,那么对于任意两个下标 ij(假设 i < j),它们之间的最短距离 dist(i, j) 可以是 j - i 或者 n - (j - i) 两者中的较小值。这是因为你可以沿着数组顺时针或逆时针移动来从一个位置到达另一个位置。

给定公式 f(i, j) = (a_i + a_j) × dist(i, j),我们的目标是最大化这个表达式的值。为了找到最优解,我们可以考虑以下步骤:

  1. 直接遍历所有可能的下标对:一种直观的方法是尝试所有可能的 (i, j) 对,并计算每个对的贡献值。这种方法的时间复杂度是 O(n^2),其中 n 是数组的长度。虽然这种方法可行,但当 n 很大时效率较低。

  2. 优化搜索过程

    • 由于我们是在寻找最大值,可以利用一些数学性质来减少不必要的计算。
    • 注意到 dist(i, j) 的取值范围是从 1 到 n/2(因为超过半圈的距离可以通过反方向达到更短的距离)。
    • 我们可以先固定 dist 的值,然后寻找满足此距离条件的最大 a_i + a_j 值。这样就可以把问题分解成几个子问题,每个子问题只需要线性时间就能解决。
  3. 具体实现

    • 对于每一个可能的 dist 值,我们创建一个新的数组 b,其中 b[k] = a[k] + a[(k + dist) % n]。这里 % n 是为了处理环形数组的特性。
    • 然后,在数组 b 中找到最大值,该最大值即对应于当前 dist 下最大的 a_i + a_j
    • 计算出当前 dist 下的最大贡献值,并更新全局最大贡献值。
  4. 细节处理

    • 当 dist 为 1 时,情况较为特殊,因为此时 i 和 j 直接相邻,这可能是最大贡献值的一个常见来源。
    • 在遍历不同 dist 时,记得考虑到 dist 和 n-dist 实际上代表了相同的物理距离,因此只需要计算一半即可。
  5. 算法复杂度分析

    • 通过上述方法,我们可以在 O(n * (n/2)) 的时间内解决问题,实际上简化为 O(n^2)。但是,通过巧妙地构建辅助数组 b 并仅执行一次线性扫描,可以将每次 dist 的处理优化至 O(n) 时间,从而整体复杂度降至 O(n^2) 的线性部分。

代码实现:

from itertools import permutations

def solution(n: int, a: list) -> int:
    max_contribution = 0
    for i in range(n):
        for j in range(n):
            contribution = (a[i] + a[j]) * min(abs(i - j), n - abs(i - j))
            max_contribution = max(max_contribution, contribution)
    return max_contribution


if __name__ == '__main__':
    print(solution(n=3, a=[1, 2, 3]) == 5)
    print(solution(n=4, a=[4, 1, 2, 3]) == 12)
    print(solution(n=5, a=[1, 5, 3, 7, 2]) == 24)
  1. 初始化最大贡献值

    max_contribution = 0
    

    这里初始化一个变量 max_contribution 为 0,用于存储在遍历过程中找到的最大贡献值。

  2. 双重循环遍历所有可能的下标对

    for i in range(n):
        for j in range(n):
    

    使用两个嵌套循环来尝试数组中的每一对下标组合 (i, j)。这里 ij 都从 0 到 n-1

  3. 计算贡献值

    contribution = (a[i] + a[j]) * min(abs(i - j), n - abs(i - j))
    

    对于每一对下标 (i, j),计算它们之间的贡献值。abs(i - j) 是按顺时针方向的距离,而 n - abs(i - j) 是逆时针方向的距离。取两者中的较小者作为 dist(i, j),然后根据公式计算贡献值。

  4. 更新最大贡献值

    max_contribution = max(max_contribution, contribution)
    

    每次计算完当前下标对的贡献值后,与已知的最大贡献值比较,如果当前贡献值更大,则更新 max_contribution

  5. 返回结果

    return max_contribution
    

    在遍历完所有可能的下标对之后,返回找到的最大贡献值。

结束

也是最后赶完了六篇的伴学笔记,前七天学习前端的字节课,后面开始跟着小册学习langchain,学到很多很多知识,刷算法题,AI练中学这个项目真的非常非常nice,感恩!