问题背景与分析
给定一个长度为 的环形数组 ,目标是找到一对下标 ,使得它们的贡献值 最大。这里 表示下标 和 之间的最短距离,对于环形数组,最短距离可以通过两种方式计算:
- 直接距离:
- 绕过数组末尾的距离:
最短距离即为 。
暴力解法
最直接的方法是通过双重循环遍历所有可能的下标对 ,计算每一对的贡献值,然后选择贡献值最大的那一对。这种方法的时间复杂度为 ,对于较小的数组是可以接受的,但对于较大的数组可能会非常慢。
def max_contribution_brute_force(n, a):
max_contrib = 0
for i in range(n):
for j in range(n):
if i != j:
# 计算最短距离
dist = min(abs(i - j), n - abs(i - j))
# 计算贡献值
contrib = (a[i] + a[j]) * dist
# 更新最大贡献值
if contrib > max_contrib:
max_contrib = contrib
return max_contrib
优化解法
为了提高效率,我们可以利用一些数学性质和数据结构来减少不必要的计算。具体来说,可以考虑以下几种方法:
方法一:前缀和与后缀和
我们可以通过预处理前缀和与后缀和来加速计算。具体步骤如下:
- 计算每个位置 的前缀和 ,表示从数组开头到位置 的元素之和。
- 计算每个位置 的后缀和 ,表示从位置 到数组末尾的元素之和。
- 对于每个位置 ,计算它与其他位置 的贡献值,利用前缀和与后缀和来快速计算 。
def max_contribution_optimized(n, a):
prefix = [0] * n
suffix = [0] * n
# 计算前缀和
for i in range(n):
prefix[i] = sum(a[:i+1])
# 计算后缀和
for i in range(n-1, -1, -1):
suffix[i] = sum(a[i:])
max_contrib = 0
for i in range(n):
for j in range(n):
if i != j:
# 计算最短距离
dist = min(abs(i - j), n - abs(i - j))
# 利用前缀和与后缀和计算 a_i + a_j
ai_plus_aj = (prefix[i] - prefix[i-1] if i > 0 else a[i]) + (suffix[j] - suffix[j+1] if j < n-1 else a[j])
# 计算贡献值
contrib = ai_plus_aj * dist
# 更新最大贡献值
if contrib > max_contrib:
max_contrib = contrib
return max_contrib
方法二:单调栈
另一种优化方法是使用单调栈来维护一个递增或递减的序列,从而快速找到每个位置 的最优匹配位置 。具体步骤如下:
- 使用单调栈维护一个递增序列,记录每个位置 及其对应的前缀和。
- 对于每个位置 ,从栈中找到最近的一个比 小的位置 ,计算贡献值并更新最大贡献值。
- 类似地,使用另一个单调栈维护一个递减序列,记录每个位置 及其对应的后缀和。
def max_contribution_monotonic_stack(n, a):
max_contrib = 0
stack = []
# 处理前缀和
for i in range(n):
while stack and a[stack[-1]] < a[i]:
j = stack.pop()
dist = min(abs(i - j), n - abs(i - j))
contrib = (a[i] + a[j]) * dist
if contrib > max_contrib:
max_contrib = contrib
stack.append(i)
# 清空栈
stack.clear()
# 处理后缀和
for i in range(n-1, -1, -1):
while stack and a[stack[-1]] < a[i]:
j = stack.pop()
dist = min(abs(i - j), n - abs(i - j))
contrib = (a[i] + a[j]) * dist
if contrib > max_contrib:
max_contrib = contrib
stack.append(i)
return max_contrib
总结
以上三种方法分别适用于不同的场景:
- 暴力解法:适用于数组较小的情况,时间复杂度为 。
- 前缀和与后缀和:通过预处理前缀和与后缀和来加速计算,时间复杂度仍为 ,但在实际运行中会更快。
- 单调栈:通过维护单调栈来快速找到最优匹配位置,时间复杂度为 ,适用于较大的数组。
选择合适的方法取决于数组的大小和性能要求。对于大多数实际应用,单调栈方法通常是最优的选择。