刷题进展之中等题-最大乘积区间问题 | 豆包MarsCode AI刷题

16 阅读4分钟

最大乘积区间问题

问题描述

小R手上有一个长度为 n 的数组 (n > 0),数组中的元素分别来自集合 [0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]。小R想从这个数组中选取一段连续的区间,得到可能的最大乘积。

你需要帮助小R找到最大乘积的区间,并输出这个区间的起始位置 x 和结束位置 y (x ≤ y)。如果存在多个区间乘积相同的情况,优先选择 x 更小的区间;如果 x 相同,选择 y 更小的区间。

注意:数组的起始位置为 1,结束位置为 n


测试样例

样例1:

输入:n = 5, arr = [1, 2, 4, 0, 8]
输出:[1, 3]

样例2:

输入:n = 7, arr = [1, 2, 4, 8, 0, 256, 0]
输出:[6, 6]

样例3:

输入:n = 8, arr = [1, 2, 4, 8, 0, 256, 512, 0]
输出:[6, 7]

解题思路

题目要求从数组中选取一段连续区间,找到该区间的最大乘积及对应的起始和结束位置。由于数组中的元素是2的幂或者0,因此可以利用以下特点优化解题:

  1. 乘积特性

    • 元素是 2k2^k2k 的形式,乘积仍是 2和2^{\text{和}}2和 的形式。乘积越大,指数和越大。
    • 若区间中包含0,乘积直接为0。因此需要在每个以0为分界点的子数组中分别计算。
  2. 滑动窗口和分段处理

    • 遇到0时,将数组划分为多个子数组。
    • 对于每个子数组,计算所有可能的区间乘积,并记录最大值和其对应的起始和结束位置。
  3. 优先级规则

    • 如果最大乘积有多个区间对应,优先选起点小的;若起点相同,选终点小的。

算法步骤

  1. 分段处理

    • 将数组按照0进行划分。每个非零段单独处理。
    • 如果数组全为0,直接返回区间长度为1的结果,且优先选择靠前的元素。
  2. 计算区间乘积

    • 用两个指针 lll 和 rrr 表示当前区间的左右端点,计算每个可能的区间乘积。
    • 记录最大乘积和其对应的 x,yx, yx,y。
  3. 比较更新最大值

    • 如果发现当前区间乘积比之前的大,更新最大值和对应的区间。
    • 如果乘积相同,按照起点和终点的优先级规则更新。
  4. 合并结果

    • 遍历所有子数组的结果,输出全局最大乘积区间。

代码实现

python
复制代码
def max_product_interval(n, arr):
    max_product = -1  # 初始化最大乘积
    best_x, best_y = 1, 1  # 初始化最佳区间
    start = 0  # 分段起点

    # 遍历整个数组,分段处理
    for i in range(n + 1):
        if i == n or arr[i] == 0:  # 遇到 0 或到数组末尾
            if start < i:  # 存在非零子段
                subarray = arr[start:i]  # 提取非零子段
                product, x, y = find_max_product_in_subarray(subarray, start)
                # 更新全局最大值和区间
                if (product > max_product or
                    (product == max_product and x < best_x) or
                    (product == max_product and x == best_x and y < best_y)):
                    max_product, best_x, best_y = product, x, y
            start = i + 1  # 更新下一个子段的起点

    return [best_x, best_y]

def find_max_product_in_subarray(subarray, offset):
    """
    计算一个非零子数组的最大乘积区间。
    返回值为 (最大乘积, 起始位置, 结束位置)。
    """
    n = len(subarray)
    max_product = -1
    best_x, best_y = 1, 1
    product = 1

    for l in range(n):  # 左端点
        product = 1  # 重置乘积
        for r in range(l, n):  # 右端点
            product *= subarray[r]
            if product > max_product:
                max_product = product
                best_x, best_y = l + offset + 1, r + offset + 1  # 转换为全局位置
            elif product == max_product:
                if l + offset + 1 < best_x or (l + offset + 1 == best_x and r + offset + 1 < best_y):
                    best_x, best_y = l + offset + 1, r + offset + 1
    return max_product, best_x, best_y

代码说明

  1. 分段处理

    • 主函数 max_product_interval 遍历数组,当遇到 0 时将当前段提取出来,调用 find_max_product_in_subarray 计算当前段的最大乘积。
  2. 区间乘积计算

    • 在每个子段内,使用嵌套循环枚举所有可能的区间,逐步累乘,并记录最大乘积和对应区间。
  3. 优先级判断

    • 更新最大值时,检查乘积是否更大。如果乘积相同,按起点和终点的规则比较。

复杂度分析

  1. 时间复杂度

    • 遍历数组一次分段 O(n)。
    • 每个子段计算区间乘积 O(k^2),其中 kkk 是子段长度。最坏情况下 k≈nk ,总体复杂度为O(n^2)。
  2. 空间复杂度

    • 使用常量额外空间,复杂度为 O(1)。

AC代码如下: