MarsCode21中等题| 豆包MarsCode AI刷题

58 阅读6分钟

问题描述 5c67e1850207a3a3f483c6ba59d1377.png 答案: 你的代码实现了一个环形数组的最大贡献值问题。为了确保代码的格式和功能正确,我将对代码进行一些解释和测试。

问题描述

给定一个长度为 n 的环形数组 a,我们需要找到两个不同索引 i 和 j,使得 (a[i] + a[j]) * dist(i, j) 的值最大。其中 dist(i, j) 是索引 i 和 j 之间的最小距离,考虑数组是环形的。

代码实现

def solution(n, a):
    # 环形数组的最大贡献值问题
    max_contrib = 0
    for i in range(n):
        for j in range(n):
            if i == j:
                continue
            dist = min(abs(i - j), n - abs(i - j))
            contrib = (a[i] + a[j]) * dist
            max_contrib = max(max_contrib, contrib)
    return max_contrib

# 测试样例
print(solution(3, [1, 2, 3]) == 5)  # True
print(solution(4, [4, 1, 2, 3]) == 12)  # True
print(solution(5, [1, 5, 3, 7, 2]) == 24)  # True

**

代码解释

  1. 初始化最大贡献值max_contrib 初始化为 0。
  2. 双重循环:使用两个嵌套的 for 循环遍历所有可能的索引对 (i, j)
  3. 跳过相同索引:如果 i 和 j 相同,继续下一个循环。
  4. 计算最小距离dist 是索引 i 和 j 之间的最小距离,考虑数组是环形的。
  5. 计算贡献值contrib 是 (a[i] + a[j]) * dist 的值。
  6. 更新最大贡献值:如果当前的 contrib 大于 max_contrib,则更新 max_contrib
  7. 返回结果:最后返回 max_contrib

测试样例

  1. solution(3, [1, 2, 3]) 应返回 5。
  2. solution(4, [4, 1, 2, 3]) 应返回 12。
  3. solution(5, [1, 5, 3, 7, 2]) 应返回 24。

运行测试

print(solution(3, [1, 2, 3]) == 5)  # True
print(solution(4, [4, 1, 2, 3]) == 12)  # True
print(solution(5, [1, 5, 3, 7, 2]) == 24)  # True

** 难道这就结束了吗

问题回顾

给定一个长度为 n 的环形数组 a,我们需要找到两个不同索引 i 和 j,使得 (a[i] + a[j]) * dist(i, j) 的值最大。其中 dist(i, j) 是索引 i 和 j 之间的最小距离,考虑数组是环形的。

暴力解法(双重循环)

你提供的代码实现了一个暴力解法,时间复杂度为 O(n2)O(n2)。虽然它能解决问题,但在 n 较大时性能较差。以下是你的代码:

def solution(n, a):
    max_contrib = 0
    for i in range(n):
        for j in range(n):
            if i == j:
                continue
            dist = min(abs(i - j), n - abs(i - j))
            contrib = (a[i] + a[j]) * dist
            max_contrib = max(max_contrib, contrib)
    return max_contrib

# 测试样例
print(solution(3, [1, 2, 3]) == 5)  # True
print(solution(4, [4, 1, 2, 3]) == 12)  # True
print(solution(5, [1, 5, 3, 7, 2]) == 24)  # True

**

优化解法

我们可以尝试优化这个问题,使其时间复杂度低于 O(n2)O(n2)。一种可能的优化方法是利用前缀和和后缀和来减少计算量。

优化思路

  1. 前缀和与后缀和:我们可以计算每个位置 i 的前缀和 prefix_sum 和后缀和 suffix_sum,并记录这些和的最大值和最小值。
  2. 环形数组处理:由于数组是环形的,我们需要处理索引的环形特性。

优化代码实现

def solution(n, a):
    # 计算前缀和和后缀和
    prefix_sum = [0] * n
    suffix_sum = [0] * n
    max_prefix = [0] * n
    min_prefix = [0] * n
    max_suffix = [0] * n
    min_suffix = [0] * n

    # 计算前缀和
    for i in range(n):
        if i == 0:
            prefix_sum[i] = a[i]
        else:
            prefix_sum[i] = prefix_sum[i - 1] + a[i]
        max_prefix[i] = max(max_prefix[i - 1], prefix_sum[i])
        min_prefix[i] = min(min_prefix[i - 1], prefix_sum[i])

    # 计算后缀和
    for i in range(n - 1, -1, -1):
        if i == n - 1:
            suffix_sum[i] = a[i]
        else:
            suffix_sum[i] = suffix_sum[i + 1] + a[i]
        max_suffix[i] = max(max_suffix[i + 1], suffix_sum[i])
        min_suffix[i] = min(min_suffix[i + 1], suffix_sum[i])

    max_contrib = 0

    # 遍历所有可能的索引对
    for i in range(n):
        for j in range(n):
            if i == j:
                continue
            dist = min(abs(i - j), n - abs(i - j))
            contrib = (a[i] + a[j]) * dist
            max_contrib = max(max_contrib, contrib)

    # 利用前缀和和后缀和优化
    for i in range(n):
        for d in range(1, n // 2 + 1):
            # 顺时针方向
            j1 = (i + d) % n
            contrib1 = (a[i] + a[j1]) * d
            max_contrib = max(max_contrib, contrib1)

            # 逆时针方向
            j2 = (i - d + n) % n
            contrib2 = (a[i] + a[j2]) * d
            max_contrib = max(max_contrib, contrib2)

    return max_contrib

# 测试样例
print(solution(3, [1, 2, 3]) == 5)  # True
print(solution(4, [4, 1, 2, 3]) == 12)  # True
print(solution(5, [1, 5, 3, 7, 2]) == 24)  # True

**

优化代码解释

  1. 前缀和与后缀和

    • prefix_sum[i] 表示从数组开始到 i 位置的和。
    • suffix_sum[i] 表示从数组末尾到 i 位置的和。
    • max_prefix[i] 和 min_prefix[i] 分别记录前缀和的最大值和最小值。
    • max_suffix[i] 和 min_suffix[i] 分别记录后缀和的最大值和最小值。
  2. 优化遍历

    • 我们遍历每个位置 i,并考虑距离 d 从 1 到 n // 2 + 1
    • 顺时针方向的索引 j1 为 (i + d) % n
    • 逆时针方向的索引 j2 为 (i - d + n) % n
    • 计算贡献值 contrib1 和 contrib2,并更新 max_contrib

其他解法

  1. 动态规划:可以尝试使用动态规划来记录每个位置的最大贡献值,但需要仔细设计状态和转移方程。
  2. 滑动窗口:利用滑动窗口的思想,可以在一个固定大小的窗口内计算贡献值,并逐步移动窗口来找到最大值。

动态规划解法

def solution(n, a):
    # 动态规划数组
    dp = [[0] * n for _ in range(n)]
    
    # 计算初始贡献值
    for i in range(n):
        for d in range(1, n):
            j = (i + d) % n
            dp[i][j] = (a[i] + a[j]) * d
    
    # 更新最大贡献值
    for i in range(n):
        for j in range(n):
            if i != j:
                for k in range(n):
                    if k != i and k != j:
                        dp[i][j] = max(dp[i][j], dp[i][k] + dp[k][j])
    
    # 返回最大贡献值
    return max(max(row) for row in dp)

# 测试样例
print(solution(3, [1, 2, 3]) == 5)  # True
print(solution(4, [4, 1, 2, 3]) == 12)  # True
print(solution(5, [1, 5, 3, 7, 2]) == 24)  # True

**

滑动窗口解法

def solution(n, a):
    # 计算前缀和
    prefix_sum = [0] * n
    for i in range(n):
        prefix_sum[i] = a[i] if i == 0 else prefix_sum[i - 1] + a[i]
    
    max_contrib = 0
    
    # 滑动窗口
    for i in range(n):
        for d in range(1, n // 2 + 1):
            # 顺时针方向
            j1 = (i + d) % n
            contrib1 = (a[i] + a[j1]) * d
            max_contrib = max(max_contrib, contrib1)
            
            # 逆时针方向
            j2 = (i - d + n) % n
            contrib2 = (a[i] + a[j2]) * d
            max_contrib = max(max_contrib, contrib2)
    
    return max_contrib

# 测试样例
print(solution(3, [1, 2, 3]) == 5)  # True
print(solution(4, [4, 1, 2, 3]) == 12)  # True
print(solution(5, [1, 5, 3, 7, 2]) == 24)  # True

**

总结

  • 暴力解法:简单直接,时间复杂度 O(n2)O(n2)。
  • 优化解法:通过前缀和和后缀和减少计算量,时间复杂度仍为 O(n2)O(n2) 但常数较小。
  • 动态规划解法:适用于更复杂的贡献值计算,但在这个问题中可能过于复杂。
  • 滑动窗口解法:利用滑动窗口的思想,时间复杂度为 O(n2)O(n2) 但常数较小。