问题分析
小S有一个长度为 n 的环形数组,环形数组的特点是首尾元素相邻。她希望找到一对下标 i,j,使得以下公式计算出的贡献值最大:
f(i,j)=(a[i]+a[j])×dist(i,j)
其中:
- dist(i,j) 表示 i 和 j 在环形数组中的最短距离。
环形数组特性
-
环形关系:首尾元素相邻。例如数组 [1,2,3],首尾距离为 1。
-
最短距离:从 i 到 j 的距离可能有两种:
- 顺时针:直接从 i 走到 j。
- 逆时针:从 i 绕过数组尾部到 j。
两种距离中取较小值即为 dist(i,j)。
目标:找到 i,j 的组合,使得贡献值最大。
解题思路
公式理解
公式 f(i,j)=(a[i]+a[j])×dist(i,j) 中:
-
贡献值的决定因素:
- a[i]+a[j]:数值和越大,贡献值越大。
- dist(i,j):距离越远,权重越大,但距离不能超过数组总长度的一半。
-
最短距离计算公式:
dist(i,j)=min((j−i)%n,(i+n−j)%n)
- 顺时针:(j−i)%n
- 逆时针:(i+n−j)%n
- 两者取最小值。
暴力解法
- 遍历所有 i,j 组合。
- 对于每对 i,j,计算 f(i,j) 并更新最大值。
- 时间复杂度为 O(n2),适合 n 较小的情况。
优化思路
-
减少重复计算:
- f(i,j)=f(j,i),只需计算 i<j 的情况,避免重复计算。
-
快速距离计算:
- 利用数组环形结构,提前计算最短距离,减少冗余运算。
代码实现
优化后的代码
def solution(n: int, a: list) -> int:
max_contribution = 0
# 遍历 i < j 的组合
for i in range(n):
for j in range(i + 1, n): # 确保 j > i,避免重复计算
# 计算最短距离
dist = min((j - i) % n, (i + n - j) % n)
# 计算贡献值
contribution = (a[i] + a[j]) * dist
# 更新最大贡献值
max_contribution = max(max_contribution, contribution)
return max_contribution
测试样例
if __name__ == '__main__':
assert solution(3, [1, 2, 3]) == 5 # 最大贡献值来自 (1, 3) 的组合
assert solution(4, [4, 1, 2, 3]) == 12 # 最大贡献值来自 (4, 3) 的组合
assert solution(5, [1, 5, 3, 7, 2]) == 24 # 最大贡献值来自 (5, 7) 的组合
代码详解
-
遍历优化:
- 只遍历 i<j 的组合,避免对称计算 f(i,j)=f(j,i)。
- 第二层循环从 j=i+1 开始。
-
最短距离计算:
- 顺时针:直接从 i 到 j,公式为 (j−i)%n。
- 逆时针:从 i 经过数组尾部到 j,公式为 (i+n−j)%n。
- 两者取最小值:
dist = min((j - i) % n, (i + n - j) % n)。
-
贡献值更新:
- 每次计算 f(i,j) 后,与当前的最大贡献值对比,更新最大值。
复杂度分析
- 时间复杂度:优化后,内层循环减少一半,复杂度降为 O(n2/2),即 O(n2),适合中等规模数据。
- 空间复杂度:除了存储常量变量
max_contribution,无需额外空间,复杂度为 O(1)。