最大乘积问题 | 豆包MarsCode AI刷题

75 阅读5分钟

问题描述

小R最近遇到了一个数组问题。他有一个包含 NN 个元素的数组,记作 a1,a2,...,aNa1​,a2​,...,aN​。为了分析这个数组的特性,小R定义了两个函数 L(i)L(i) 和 R(i)R(i),并希望通过这两个函数来找到一些有趣的结论。

  • L(i)L(i) 是满足以下条件的最大的 jj 值:
  • j<ij<i
  • a[j]>a[i]a[j]>a[i]
  • 如果找不到这样的 jj,那么 L(i)=0L(i)=0;如果有多个满足条件的 jj,选择离 ii 最近的那个。
  • R(i)R(i) 是满足以下条件的最小的 kk 值:
  • k>ik>i
  • a[k]>a[i]a[k]>a[i]
  • 如果找不到这样的 kk,那么 R(i)=0R(i)=0;如果有多个满足条件的 kk,选择离 ii 最近的那个。

最终,小R定义 MAX(i)=L(i)∗R(i)MAX(i)=L(i)∗R(i),他想知道在 1≤i≤N1≤i≤N 的范围内,MAX(i)MAX(i) 的最大值是多少。

测试样例

样例1:

输入:n = 5, array = [5, 4, 3, 4, 5]
输出:8

样例2:

输入:n = 6, array = [2, 1, 4, 3, 6, 5]
输出:15

样例3:

输入:n = 7, array = [1, 2, 3, 4, 5, 6, 7]
输出:0


问题解析:

需要分析数组中的每个元素,找到它左右两侧第一个比它大的元素,并通过这两个元素的索引计算最大值。

关键点:

  1. L(i) 的定义:

    • 在当前位置 i 左侧找到第一个比 array[i] 大的元素。
    • 如果不存在,则 L(i) = 0
    • 否则,L(i) 是这个元素的索引(从1开始计数)。
  2. R(i) 的定义:

    • 在当前位置 i 右侧找到第一个比 array[i] 大的元素。
    • 如果不存在,则 R(i) = 0
    • 否则,R(i) 是这个元素的索引(从1开始计数)。
  3. MAX(i) 的定义:

    • MAX(i) = L(i) * R(i)
    • 最终要找到所有 MAX(i) 中的最大值。

实现思路:

  1. 初始化数组 LR

    • 创建两个数组 LR,分别存储每个位置的 L(i)R(i)
  2. 使用单调栈计算 L(i)

    • 遍历数组,对于每个元素,利用单调栈找到左侧第一个比当前元素大的值。
    • 如果栈顶元素比当前元素小或相等,弹出栈顶元素。
    • 栈顶元素即为左侧第一个比当前元素大的元素。
  3. 使用单调栈计算 R(i)

    • 从右向左遍历数组,逻辑类似计算 L(i)
    • 使用单调栈找到右侧第一个比当前元素大的值。
  4. 计算 MAX(i) 并求最大值:

    • 遍历所有位置,计算 MAX(i) 并记录最大值。

代码分析:

def solution(n, array):
    # 初始化 L 和 R 数组
    L = [0] * n  # L[i] 存储第一个左侧比 array[i] 大的元素的索引
    R = [0] * n  # R[i] 存储第一个右侧比 array[i] 大的元素的索引
    
    # 使用单调栈计算 L(i)
    stack = []  # 存储索引的单调栈
    
    for i in range(n):
        # 移除栈中比当前元素小的元素
        while stack and array[stack[-1]] <= array[i]:
            stack.pop()
                                                    
        # 栈顶元素是第一个比 array[i] 大的左侧元素
        L[i] = stack[-1] + 1 if stack else 0
        stack.append(i)
    
    # 使用单调栈计算 R(i)
    stack = []
                                                    
    for i in range(n - 1, -1, -1):  # 从右向左遍历
        while stack and array[stack[-1]] <= array[i]:
            stack.pop()
    
        # 栈顶元素是第一个比 array[i] 大的右侧元素
        R[i] = stack[-1] + 1 if stack else 0
        stack.append(i)
    
    # 计算 MAX(i) 的最大值
    max_value = 0
    
    for i in range(n):
        max_value = max(max_value, L[i] * R[i])
    
    return  max_value

复杂度分析:

  1. 时间复杂度:

    • 计算 LR 的过程各遍历数组一次,每个元素最多被压入和弹出栈一次,时间复杂度为 O(n)。
    • 最终求 MAX(i) 最大值的循环也是 O(n)。
    • 总时间复杂度: O(n)。
  2. 空间复杂度:

    • 额外使用了两个数组 LR,每个大小为 O(n)。
    • 使用单调栈存储索引,栈的最大深度为 O(n)。
    • 总空间复杂度: O(n)。

最大乘积问题解析

问题描述:

小R有一个包含 NNN 个元素的数组 a1,a2,...,aNa_1, a_2, ..., a_Na1​,a2​,...,aN​。他定义了两个函数 L(i)L(i)L(i) 和 R(i)R(i)R(i):

  • L(i)L(i)L(i):在当前位置 iii 左侧找到第一个比 a[i]a[i]a[i] 大的元素索引(从1开始)。如果没有,返回0。
  • R(i)R(i)R(i):在当前位置 iii 右侧找到第一个比 a[i]a[i]a[i] 大的元素索引(从1开始)。如果没有,返回0。

最终定义 MAX(i)=L(i)×R(i)MAX(i) = L(i) \times R(i)MAX(i)=L(i)×R(i),要求找到所有 MAX(i)MAX(i)MAX(i) 中的最大值。

示例:

  • 输入:n = 5, array = [5, 4, 3, 4, 5]
  • 输出:8

知识总结

  1. 单调栈

    • 用于在O(N)时间复杂度内求解下一个更大/更小元素问题。
    • 针对这个问题,单调栈可以帮助快速找到左侧和右侧第一个比当前元素大的索引。
  2. 优化思路

    • 采用单调递减栈分别从左到右和从右到左遍历数组,记录每个位置L(i)R(i)
    • 最终遍历数组,计算所有 MAX(i),取最大值。

学习计划

  • 学习单调栈的原理及其应用场景,结合动态规划或滑动窗口,尝试解决复杂的栈应用题目。

  • 对错题进行详细总结,分析错误原因,记录解法和注意点。


工具运用

  • 错题分析: 使用 豆包 AI 生成的详细解析帮助理解错题,能够快速找到解题关键点。
  • 知识学习: 通过 豆包 AI 解析题目后,遇到不懂的概念,也可以结合其他学习资料去学习并理解。