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

100 阅读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]

思路分析:

  1. 由于数组中的元素可能包含 0,而 0 与任何数相乘都为 0,所以当遇到 0 时,会将之前连续区间的乘积 “截断”。因此,我们不能简单地使用动态规划的乘积形式来处理整个数组,而是需要分段处理。

  2. 我们可以从数组的开头开始,依次遍历数组。每次遇到 0 时,就将当前的连续区间乘积重置为 1,并重新开始计算下一段连续区间的乘积。

  3. 在每一段连续区间内(即两个 0 之间或者从开头到第一个 0 之前,从最后一个 0 之后到结尾),我们通过动态规划的思想来计算以每个位置结尾的最大乘积和最小乘积(因为数组中可能存在负数,负数乘以负数可能得到更大的正数)。

    • 对于每个位置 i,以它结尾的最大乘积 maxEndingHere 可能是以下三种情况之一:

      • 当前元素 arr[i] 本身(例如当前区间之前的乘积为负数,而当前元素也是负数,那么相乘后可能得到更大的正数)。
      • 之前位置的最大乘积 maxEndingHere[i - 1] 乘以当前元素 arr[i](如果之前的最大乘积是正数,当前元素也是正数,相乘会得到更大的正数)。
      • 之前位置的最小乘积 minEndingHere[i - 1] 乘以当前元素 arr[i](当之前的最小乘积是负数,当前元素也是负数时,相乘会得到正数,且可能比其他两种情况更大)。
    • 同理,以每个位置 i 结尾的最小乘积 minEndingHere 也有类似的三种情况,只是取最小值而已。

  4. 在每一段连续区间内,记录下该区间内的最大乘积 maxProductInSegment 以及对应的起始位置和结束位置。

  5. 比较所有连续区间的最大乘积,最终得到整个数组中的最大乘积以及对应的区间位置。

代码实现:

def solution(n, arr):
    overall_max_product = float('-inf')
    overall_start_index = 0
    overall_end_index = 0

    segment_start_index = 0
    max_product_in_segment = 1
    min_product_in_segment = 1

    for i in range(n):
        if arr[i] == 0:
            # 处理完一段非零连续区间后,更新整体结果
            if max_product_in_segment > overall_max_product:
                overall_max_product = max_product_in_segment
                overall_start_index = segment_start_index
                overall_end_index = i - 1
            elif max_product_in_segment == overall_max_product:
                # 处理多个相同乘积的情况
                if segment_start_index < overall_start_index or (segment_start_index == overall_start_index and i - 1 < overall_end_index):
                    overall_start_index = segment_start_index
                    overall_end_index = i - 1

            # 重置为下一段非零连续区间做准备
            max_product_in_segment = 1
            min_product_in_segment = 1
            segment_start_index = i + 1
            continue

        # 保存当前的最大和最小乘积,用于计算新的最大和最小乘积
        prev_max_product = max_product_in_segment
        prev_min_product = min_product_in_segment

        max_product_in_segment = max(
            arr[i],
            arr[i] * prev_max_product,
            arr[i] * prev_min_product
        )
        min_product_in_segment = min(
            arr[i],
            arr[i] * prev_max_product,
            arr[i] * prev_min_product
        )

        # 如果当前元素结尾的最大乘积是新的区间最大乘积,更新区间结束位置
        if max_product_in_segment > overall_max_product:
            overall_max_product = max_product_in_segment
            overall_start_index = segment_start_index
            overall_end_index = i
        elif max_product_in_segment == overall_max_product:
            # 处理多个相同乘积的情况
            if segment_start_index < overall_start_index or (segment_start_index == overall_start_index and i < overall_end_index):
                overall_start_index = segment_start_index
                overall_end_index = i

    # 处理最后一段非零连续区间(如果有的话)
    if max_product_in_segment > overall_max_product:
        overall_max_product = max_product_in_segment
        overall_start_index = segment_start_index
        overall_end_index = n - 1
    elif max_product_in_segment == overall_max_product:
        # 处理多个相同乘积的情况
        if segment_start_index < overall_start_index or (segment_start_index == overall_start_index and n - 1 < overall_end_index):
            overall_start_index = segment_start_index
            overall_end_index = n - 1

    return [overall_start_index + 1, overall_end_index + 1]

if __name__ == "__main__":
    # Add your test cases here
    print(solution(5, [1, 2, 4, 0, 8]) == [1, 3])
    print(solution(7, [1, 2, 4, 8, 0, 256, 0]) == [6, 6])