学习笔记:环形数组中的最大贡献值
题目解析
1. 问题描述
小S需要找到环形数组中的一对下标 (i,j)(i, j),使得贡献值公式:
f(i,j)=(ai+aj)×dist(i,j)f(i, j) = (a_i + a_j) \times \text{dist}(i, j)
的值尽可能大,其中 dist(i,j)\text{dist}(i, j) 表示两个下标之间的最短距离(注意环形数组的特性)。
分析
2. 思路
-
计算最短距离 dist(i,j)\text{dist}(i, j) :
-
两个下标之间的距离有两种情况:
- ∣i−j∣|i - j|,即普通数组的距离。
- n−∣i−j∣n - |i - j|,即通过环形另一侧的距离。
-
取两者的较小值:dist(i,j)=min(∣i−j∣,n−∣i−j∣)\text{dist}(i, j) = \min(|i - j|, n - |i - j|)。
-
-
暴力求解:
- 枚举所有可能的下标对 (i,j)(i, j),计算贡献值。
- 复杂度为 O(n2)O(n^2),适用于小规模数据。
-
优化方向(未实现) :
- 对称性利用:环形数组可以映射到线性数组中,避免重复计算。
- 滑动窗口:在某些条件下,可以通过固定窗口快速计算一组下标的贡献值。
3. 示例分析
示例1:n=3, a=[1, 2, 3]
-
数组长度为 3,环形特性使得任意两下标的距离均为 1。
-
贡献值计算:
- f(0,1)=(1+2)×1=3f(0, 1) = (1 + 2) \times 1 = 3
- f(0,2)=(1+3)×1=4f(0, 2) = (1 + 3) \times 1 = 4
- f(1,2)=(2+3)×1=5f(1, 2) = (2 + 3) \times 1 = 5
-
最大贡献值:5。
代码实现
def solution(n: int, a: list) -> int:
# 当数组长度为1时,无法形成下标对
if n == 1:
return 0
max_contribution = float('-inf') # 初始化最大贡献值为负无穷
# 暴力枚举所有下标对
for i in range(n):
for j in range(i + 1, n): # 只需计算一次 (i, j) 和 (j, i)
# 计算最短距离
dist = min(abs(i - j), n - abs(i - j))
# 计算贡献值
contribution = (a[i] + a[j]) * dist
# 更新最大贡献值
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)
复杂度分析
-
时间复杂度:
- 外层循环遍历所有元素 ii,内层遍历 i+1i+1 到 n−1n-1,总计 O(n2)O(n^2)。
- 时间复杂度为 O(n2)O(n^2) 。
-
空间复杂度:
- 仅使用常数级别的额外空间,复杂度为 O(1)O(1) 。
知识总结
1. 环形数组的特点
- 环形数组是一种特殊数据结构,其中首尾元素相连。
- 下标之间的距离有两种路径,需要根据实际情况计算最短路径: dist(i,j)=min(∣i−j∣,n−∣i−j∣)\text{dist}(i, j) = \min(|i - j|, n - |i - j|)
2. 枚举法
- 枚举法适合求解下标对关系问题,如本题中的最大贡献值。
- 时间复杂度较高,但实现简单,适用于小规模数据。
3. 动态优化
- 对于大规模数据,可以通过动态规划或滑动窗口优化计算。
- 优化的核心是减少不必要的重复计算,例如利用对称性或累计和。
学习建议
- 打好基础:对于初学者,建议熟练掌握枚举法解决问题,理解暴力法的实现逻辑和适用场景。
- 尝试优化:了解优化方向,如利用对称性、滑动窗口等方法,逐步提高效率。
- 多练习环形问题:环形数组问题常见于各种题型(如环形子数组、环形最大值),需要熟悉其特性和解法。