刷题记录3 | 豆包MarsCode AI刷题

33 阅读5分钟

问题描述

小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

这道题的关键在于如何高效地找到满足条件的 ii 和 jj,使得 f(i,j)f(i,j) 最大。由于 nn 可能很大,不能对所有的 O(n2)O(n2) 对进行暴力计算。因此,需要找到优化的策略。

观察贡献值公式:

f(i,j)=(ai+aj)×dist(i,j)f(i,j)=(ai​+aj​)×dist(i,j)

贡献值取决于两个因素:

  1. 元素之和 ai+ajai​+aj​:  数值越大越好。
  2. 下标距离 dist(i,j)dist(i,j):  距离越大越好(因为乘积会更大)。

因此,要使贡献值最大,需要在 元素之和 和 下标距离 之间取得平衡。

策略一:固定距离,寻找最大元素之和

考虑固定一个距离 dd,然后在这个距离下寻找元素之和最大的 ai+ajai​+aj​。

  • 对于每个可能的距离 dd,遍历所有的 ii,计算 j=(i+d)%nj=(i+d)%n。
  • 计算 s=ai+ajs=ai​+aj​,并记录该距离下的最大 ss。
  • 贡献值为 f=s×df=s×d。

优点:  可以针对较大的距离 dd 进行优化,因为距离越大,乘积可能越大。

策略二:固定元素之和,寻找最大距离

反过来,考虑数组中较大的元素:

  • 将数组元素按值从大到小排序,记录对应的下标。
  • 从中选取前 KK 个最大的元素(KK 是一个合适的常数,如 500)。
  • 枚举这些元素之间的所有可能组合,计算它们的下标距离和贡献值。
  • 由于元素值较大,尽管距离可能较小,乘积仍可能较大。

优点:  利用大元素的优势,可能在较小的距离下获得较大的贡献值。

综合策略

结合上述两种策略:

  1. 针对较大的距离,寻找元素之和最大的配对。
  2. 针对较大的元素,寻找距离最大的配对。

通过对前 KK 个较大的距离和前 KK 个较大的元素进行组合,寻找可能的最大贡献值。

解题:

# 定义要考虑的前 K 个元素或距离
K = 500  # 根据实际情况调整 K 值,500 是一个安全的默认值
# 策略一:处理前 K 大元素的配对
# 将元素和下标一起存储
indices_and_values = list(enumerate(a))
# 按照元素值从大到小排序
indices_and_values.sort(key=lambda x: -x[1])
max_f = 0  # 初始化最大贡献值
# 选取前 K 个最大的元素
top_k = indices_and_values[:K]
# 遍历这些元素的所有可能配对
for i in range(len(top_k)):
    idx_i, val_i = top_k[i]
    for j in range(i + 1, len(top_k)):
        idx_j, val_j = top_k[j]
        # 计算最短距离
        dist = min(abs(idx_i - idx_j), n - abs(idx_i - idx_j))
        # 计算贡献值
        f = (val_i + val_j) * dist
        if f > max_f:
            max_f = f  # 更新最大贡献值

# 策略二:处理前 K 大的距离
max_possible_distance = n // 2  # 最大可能的距离
for d in range(max_possible_distance, max_possible_distance - K, -1):
    if d <= 0:
        break
    max_s = 0  # 当前距离下的最大元素之和
    for i in range(n):
        j = (i + d) % n
        s = a[i] + a[j]
        if s > max_s:
            max_s = s  # 更新最大元素之和
    f = max_s * d  # 计算贡献值
    if f > max_f:
        max_f = f  # 更新最大贡献值

return max_f

1. 时间复杂度的平衡

由于 n 可能非常大,无法进行 O(n2) 的遍历。因此,需要找到一个折中的方法,在保证计算精度的同时,控制算法的时间复杂度。

选择一个合适的K值,可以在 O(K2+K×n) 的时间复杂度内完成计算。

2. 利用数据特性

  • 大元素的影响力:  数组中较大的元素对贡献值的影响更大,因此优先考虑它们的组合是合理的。
  • 大距离的优势:  距离越大,乘积可能越大,因此在距离上也需要进行重点考虑。

3. 环形数组的特性

环形数组使得首尾元素相邻,这对于计算下标距离有重要影响。在计算最短距离时,需要使用:

dist(i,j)=min⁡(∣i−j∣,n−∣i−j∣)

这一点在编码时需要特别注意。

4. 参数 K 的选择

  • 过小的 K:  可能会遗漏最大的贡献值。
  • 过大的 K:  会增加算法的时间复杂度,可能导致超时。

因此,需要根据具体的数据规模和时间限制,选择一个合适的 K 值。

5. 可能的优化方向

  • 并行计算:  如果环境支持,可以利用多线程或多进程加速计算。
  • 进一步的数学分析:  试图找到贡献值的上界,或者寻找特定条件下的最优解。