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

81 阅读3分钟

问题描述

小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]


解题思路

暴力做法是,记录当前的最大乘积以及对应的区间。对于每一个可能的区间起点 i,我们尝试扩展区间直到遇到 0 为止。在遍历过程中,不断更新最大乘积以及对应的区间。但由于直接计算乘积,可能会导致以下问题:

  • 如果数组中的元素较大或区间较长,直接计算乘积可能会超出 long long 类型的表示范围(从263263-2^{63}到2^{63}约为9×10189\times10^{18}),导致 整数溢出
  • 整数溢出会导致计算结果不准确,进而导致算法输出错误的区间。

使用对数避免整数溢出
由于直接计算子区间的乘积可能导致溢出(例如,当数组元素很大或区间很长时),代码通过对数组元素取对数,将乘法转化为加法: log(a×b)=log(a)+log(b)log(a×b)=log(a)+log(b) 使用 current_log_sum 来维护当前子区间的对数和。
处理0的特殊情况
当数组元素为 0 时,子区间的乘积无论如何都会是 0,因此将 current_log_sum 重置为 0,并将区间的起点更新到下一位置。
滑动窗口动态更新最大值
代码维护了 res_log(全局最大对数和),以及对应的区间起点和终点(lr)。如果当前子区间的对数和大于 res_log,则更新最大值及其区间。如果当前对数和等于最大值,则按照题目要求更新区间起点和终点,优先选择起点更小的区间;若起点相同,则选择终点更小的区间。

  1. 初始化
  • 使用 double 类型的变量来存储对数和,例如 current_log_sumres_logcurrent_log_sum包含了所有以当前右端点为终点的子数组的累积乘积。
  • res_log 初始化为非常小的负数,例如 -1e20
  1. 遍历数组
  • 对于每个元素,如果值为 0,则重置 current_log_sum 并更新起始位置。
  • 如果值不为 0,则计算其对数,并累加到 current_log_sum
  1. 更新最大值和区间
  • 比较 current_log_sumres_log,如果更大则更新最大值和对应的区间。
  • 按题目要求,处理起始位置和结束位置相同的情况(多个区间的乘积相同时,优先选择起点更小的区间,起点相同时选择终点更小的区间)。

最终代码

vector<int> solution(int n, vector<int> arr) {
    double res_log = -1e20; // 初始化为很小的数
    int l = 1, r = 1;
    int start = 0;
    double current_log_sum = 0;
    for (int i = 0; i < n; i++) {
        if (arr[i] == 0) {
            current_log_sum = 0;
            start = i + 1;
            continue;
        }
        current_log_sum += log(arr[i]);
        if (current_log_sum > res_log) {
            res_log = current_log_sum;
            l = start + 1;
            r = i + 1;
        } else if (current_log_sum == res_log) {
            if (start + 1 < l) {
                l = start + 1;
                r = i + 1;
            } else if (start + 1 == l && i + 1 < r) {
                r = i + 1;
            }
        }
    }
    return {l, r};
}

运行结果

image.png