环形数组中的最大贡献值 - MarsCode

102 阅读3分钟

环形数组中的最大贡献值 - MarsCode

问题描述

小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. 理解环形数组

环形数组意味着数组的第一个元素和最后一个元素是相邻的。因此,对于任意两个下标 i 和 j,它们之间的最短距离可以是:

  • 直接距离:abs(i - j)
  • 环形距离:n - abs(i - j)

其中 n 是数组的长度。

2. 贡献值公式

题目中给出的贡献值公式为:
[ f(i, j) = (a_i + a_j) \times \text{dist}(i, j) ]
其中 dist(i, j) 是 i 和 j 之间的最短距离。

3. 计算最短距离

对于任意两个下标 i 和 j,最短距离 dist(i, j) 可以通过以下公式计算:
[ \text{dist}(i, j) = \min(\text{abs}(i - j), n - \text{abs}(i - j)) ]

4. 遍历所有可能的组合

为了找到最大贡献值,我们需要遍历所有可能的下标组合 (i, j),并计算每个组合的贡献值。具体步骤如下:

  • 使用两层循环遍历所有可能的下标对 (i, j)
  • 对于每一对 (i, j),计算 dist(i, j)
  • 计算 f(i, j) 并更新最大贡献值。

5. 优化思路

虽然暴力遍历所有组合的时间复杂度是 (O(n^2)),但对于较小的 n 来说是可以接受的。如果 n 非常大,可以考虑进一步优化,例如通过预处理数组中的最大值和最小值来减少计算量。

总结

通过理解环形数组的特性,计算最短距离,并遍历所有可能的下标组合,我们可以找到最大贡献值。这个思路可以帮助你实现代码,确保正确性和效率。

通过代码:

#include <iostream>
#include <vector>
#include <string>
#include<cmath>

using namespace std;

int solution(int n, std::vector<int> a) {
    // write code here
    int mx = 0;
    for(int i = 0 ; i < n;i++){
        for(int j = 0; j < n;j++){
            mx = max(mx,(a[i]+a[j])*min(abs(j - i),n -abs(j-i)));
        }
    }
    return mx; // Placeholder return
}

int main() {
    std::cout << (solution(3, {1, 2, 3}) == 5) << std::endl;
    std::cout << (solution(4, {4, 1, 2, 3}) == 12) << std::endl;
    std::cout << (solution(5, {1, 5, 3, 7, 2}) == 24) << std::endl;
    return 0;
}

怎么把O(n*n)的复杂度算法优化呢?

要优化这个问题的算法,使其时间复杂度从 (O(n^2)) 降低到更低的复杂度,我们可以考虑以下几种方法:

1. 预处理最大值和最小值

我们可以预处理数组中的最大值和最小值,以便在计算贡献值时减少不必要的计算。

2. 利用对称性

由于环形数组的特性,贡献值公式 (f(i, j)) 和 (f(j, i)) 是相同的。因此,我们可以只计算一半的组合,然后乘以2来减少计算量。

3. 动态规划

我们可以使用动态规划的思想来存储中间结果,避免重复计算。

4. 优先队列或堆

我们可以使用优先队列或堆来维护当前的最大贡献值,从而减少不必要的比较。