问题描述
小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)
贡献值取决于两个因素:
- 元素之和 ai+ajai+aj: 数值越大越好。
- 下标距离 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)。
- 枚举这些元素之间的所有可能组合,计算它们的下标距离和贡献值。
- 由于元素值较大,尽管距离可能较小,乘积仍可能较大。
优点: 利用大元素的优势,可能在较小的距离下获得较大的贡献值。
综合策略
结合上述两种策略:
- 针对较大的距离,寻找元素之和最大的配对。
- 针对较大的元素,寻找距离最大的配对。
通过对前 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. 可能的优化方向
- 并行计算: 如果环境支持,可以利用多线程或多进程加速计算。
- 进一步的数学分析: 试图找到贡献值的上界,或者寻找特定条件下的最优解。