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

118 阅读4分钟

一、问题分析

  1. 目标
    在给定长度为 n 的数组中(数组元素来自特定集合),找到一段连续区间使得其元素乘积最大,并按照规则输出该区间的起始位置 x 和结束位置 y(优先选择 x 更小的区间,若 x 相同则选 y 更小的区间)。

  2. 关键要点

    • 在计算过程中需要处理 0 的情况。
    • 需要确定最大乘积的区间。
    • 值相同情况下,选择 x 更小的区间,若 x 相同则选 y 更小的区间
    • 乘积可能会发生越界

二、解题思路

动态规划思想

主要的是选择区间和优化乘积越界问题

基本思路是通过利用对数的性质,将乘法运算转化为加法运算(因为log(a * b) = log(a) + log(b)),以此来更方便地计算连续区间元素的乘积情况,进而找出乘积最大的区间,并返回该区间的起始位置和结束位置。

代码详细分析

    public static int[] solution(int n, int[] arr) {
        // Edit your code here
        double currentLogProduct = 0.0;
        double maxLogProduct = Double.NEGATIVE_INFINITY;
        int[] maxInterval = new int[2];
        maxInterval[0] = 1;
        maxInterval[1] = 1;
        int start = 1; // 区间起始位置

        for (int i = 0; i < n; i++) {
            if (arr[i] != 0) {
                currentLogProduct += Math.log(arr[i]);
            } else {
                // 处理当前区间的乘积
                if (currentLogProduct > maxLogProduct) {
                    maxLogProduct = currentLogProduct;
                    maxInterval[0] = start;
                    int j = i - 1;
                    while (j >= 0 && arr[j] == 1) {
                        j--;
                    }
                    maxInterval[1] = j + 1;
                }

                // 重置当前乘积和起始位置
                currentLogProduct = 0.0;
                start = i + 2; // 下一个区间的起始位置
            }
        }

        // 处理最后一个区间的乘积
        if (currentLogProduct > maxLogProduct) {
            maxLogProduct = currentLogProduct;
            maxInterval[0] = start;
            int j = n - 1;
            while (arr[j] == 1) {
                j--;
            }
            maxInterval[1] = j + 1;
        }

        return maxInterval;
    }
  1. 变量定义部分
  • currentLogProduct:用于记录当前正在考虑的连续区间内元素对数乘积的累加值。每当遇到非零元素时,就将其对数累加到这个变量上,以此来模拟区间元素的乘积情况(通过对数形式)。
  • maxLogProduct:记录已经遍历过的所有区间中最大的对数乘积值,初始化为负无穷大,确保在后续比较中,任何有效的区间对数乘积都能比它大从而更新它。
  • maxInterval:长度为 2 的整数数组,用于存储最终找到的最大乘积区间的起始位置和结束位置,初始化为[1, 1],表示默认的区间起始和结束都是 1(后续会根据实际情况更新)。
  • start:记录当前正在考虑的区间的起始位置,初始化为 1,对应题目中数组位置从 1 开始计数的要求。
  1. 非零元素处理逻辑(循环体内部)

当数组中当前元素arr[i]不为 0 时,将该元素的自然对数值累加到currentLogProduct变量上,这样通过对数的累加就等效于对原区间元素进行乘积运算(利用对数性质)。

  1. 零元素处理逻辑(循环体内部)
  • 当遇到数组元素为 0 时,意味着当前正在考虑的区间结束了,需要进行如下操作:

  • 首先,判断当前区间(截止到这个 0 元素之前)的对数乘积currentLogProduct是否大于已记录的最大对数乘积maxLogProduct。如果是,则更新maxLogProduct为当前区间的对数乘积值,同时更新maxInterval数组,将起始位置设为start(当前区间的起始位置),结束位置通过一个内层while循环来确定。这个while循环是从当前 0 元素的前一个位置i - 1开始往前找,只要遇到元素为 1 就继续往前,直到找到不为 1 的元素(因为 1 乘任何数都不改变乘积大小,所以要跳过连续的 1 来确定真正有效的区间结束位置),最终将结束位置设为找到的位置j + 1

  • 接着,重置currentLogProduct为 0.0,表示开始考虑下一个区间的乘积了,同时将start更新为i + 2,即下一个区间的起始位置(跳过当前的 0 元素以及它后面可能的连续 1 元素)。

  1. 处理最后一个区间的乘积部分(循环结束后)

在遍历完整个数组后,还需要单独处理最后一个区间(因为最后一个区间结束时不会再遇到 0 元素来触发上述的区间处理逻辑了)。同样判断最后一个区间的对数乘积currentLogProduct是否大于已记录的最大对数乘积maxLogProduct,如果是,则更新maxLogProduct以及maxInterval数组的起始和结束位置,结束位置的确定方式和前面遇到 0 元素时处理最后一个非零元素区间的方式类似,通过从数组末尾往前找,跳过连续的 1 元素来确定真正的区间结束位置。

最后,将记录着最大乘积区间起始位置和结束位置的maxInterval数组作为结果返回,供调用者获取找到的最大乘积区间信息。