学习方法与心得 2| 豆包MarsCode AI刷题

96 阅读5分钟

问题描述:

小S拿到了一个长度为 nnn 的环形数组,并定义了两个下标 iii 和 jjj 的贡献值公式为:

f(i,j)=(ai+aj)×dist(i,j)f(i, j) = (a_i + a_j) \times \text{dist}(i, j)f(i,j)=(ai​+aj​)×dist(i,j)

其中 dist(i,j)\text{dist}(i, j)dist(i,j) 是下标 iii 和 jjj 在数组中的最短距离。环形数组的特点是最左和最右的元素也是相邻的,因此需要考虑这一点。在这个问题中,目标是找到一对下标,使得它们的贡献值 f(i,j)f(i, j)f(i,j) 最大。

思路解析:

本题本质上是一个组合问题,要求在环形数组中选择一对不同的下标 iii 和 jjj,使得它们的贡献值 f(i,j)f(i, j)f(i,j) 最大。主要难点在于如何计算最短距离 dist(i,j)\text{dist}(i, j)dist(i,j),因为数组是环形的,即第一个元素和最后一个元素也是相邻的。

  1. 计算最短距离:对于任意两个下标 iii 和 jjj,最短距离可以通过以下公式计算:

    dist(i,j)=min⁡(∣i−j∣,n−∣i−j∣)\text{dist}(i, j) = \min(\left|i - j\right|, n - \left|i - j\right|)dist(i,j)=min(∣i−j∣,n−∣i−j∣)

    其中 ∣i−j∣|i - j|∣i−j∣ 是两者的普通距离,而 n−∣i−j∣n - |i - j|n−∣i−j∣ 是环形的距离。我们选择两者中较小的值作为最短距离。

  2. 计算贡献值:贡献值 f(i,j)f(i, j)f(i,j) 的计算公式为:

    f(i,j)=(ai+aj)×dist(i,j)f(i, j) = (a_i + a_j) \times \text{dist}(i, j)f(i,j)=(ai​+aj​)×dist(i,j)

  3. 遍历所有可能的下标对:我们需要遍历所有可能的下标对 iii 和 jjj(i≠ji \neq ji=j),计算对应的贡献值,并找出最大贡献值。

代码实现:

python
def solution(n: int, a: list) -> int:
    max_contribution = 0

    for i in range(n):
        for j in range(n):
            if i != j:
                # 计算最短距离
                dist_ij = min(abs(i - j), n - abs(i - j))
                # 计算贡献值
                contribution = (a[i] + a[j]) * dist_ij
                # 更新最大贡献值
                max_contribution = max(max_contribution, contribution)

    return max_contribution

时间复杂度分析:

  • 外层循环遍历数组中的每个元素(共 nnn 个元素)。
  • 内层循环同样遍历数组中的每个元素,因此总的时间复杂度为 O(n2)O(n^2)O(n2)。
  • 对于每一对 iii 和 jjj,我们计算它们之间的最短距离并计算贡献值,这些操作都是常数时间操作。

因此,整个算法的时间复杂度为 O(n2)O(n^2)O(n2)。

优化思路:

目前的解法时间复杂度为 O(n2)O(n^2)O(n2),在 nnn 较大的情况下可能会表现较差。为了优化该问题,可以考虑使用以下几种方法:

  1. 减少不必要的重复计算:当前的解法每次都计算最短距离并比较贡献值,实际上可以通过一些方法避免不必要的计算。具体来说,我们可以利用预计算来避免重复计算最短距离。
  2. 利用数据结构优化:我们可以通过一些数据结构优化方法(例如堆或优先队列),来减少寻找最大贡献值的复杂度。然而,由于本题的环形数组特性,这样的优化方案并不总是适用,因此需要谨慎考虑。
  3. 减少遍历次数:注意到 f(i,j)f(i, j)f(i,j) 的计算是对称的,即 f(i,j)=f(j,i)f(i, j) = f(j, i)f(i,j)=f(j,i),因此我们只需要遍历一半的下标对即可,避免重复计算。

改进后的代码实现:

python
def solution(n: int, a: list) -> int:
    max_contribution = 0

    # 遍历所有 i 和 j, 只计算 i < j 的情况
    for i in range(n):
        for j in range(i + 1, n):
            # 计算最短距离
            dist_ij = min(abs(i - j), n - abs(i - j))
            # 计算贡献值
            contribution = (a[i] + a[j]) * dist_ij
            # 更新最大贡献值
            max_contribution = max(max_contribution, contribution)

    return max_contribution

时间复杂度分析:

  • 外层循环遍历数组中的每个元素(共 nnn 个元素)。
  • 内层循环遍历每个元素后面的元素,因此内层循环的总次数为 n(n−1)2\frac{n(n-1)}{2}2n(n−1)​,即时间复杂度为 O(n2)O(n^2)O(n2)。
  • 在优化后的代码中,虽然减少了对称计算,但最坏情况下时间复杂度仍为 O(n2)O(n^2)O(n2),但是常数项减少了。

知识总结与心得:

  • 环形数组处理:理解环形数组的特性,并通过最短距离公式计算任意两个下标的距离。环形数组的距离计算是这道题的关键,掌握这一点是解题的核心。
  • 暴力解法的可行性:对于该问题,暴力解法 O(n2)O(n^2)O(n2) 是直接且有效的,尤其在数组长度较小时。虽然时间复杂度较高,但在小数据规模下是可接受的。
  • 逐步优化算法:理解如何优化算法的时间复杂度,提升效率。优化的空间主要来自于减少重复计算,或者通过一些数据结构来加速计算过程。

对入门同学的建议:

  1. 理解环形数组:掌握环形数组的基本概念和处理方法,尤其是如何计算元素之间的最短距离。
  2. 从暴力解法开始:首先实现一个暴力解法,确保对问题的基本理解,然后逐步学习如何优化性能。
  3. 多做练习:通过不断练习不同类型的数组问题,提升自己的算法思维。

总结:

通过这道题的学习,我加深了对环形数组的理解,并学会了如何计算数组元素之间的最短距离。尽管暴力解法在小规模数据下有效,但对于大规模数据的应用仍需考虑性能优化。希望我的学习心得能对其他同学有所帮助,祝大家在编程的道路上不断进步!