刷题笔记:447最大乘积区间问题 | 豆包MarsCode AI刷题

41 阅读3分钟

问题描述

小R正在处理一个数组序列,他的任务是找出一个区间,使得这个区间的所有数经过以下计算得到的值是最大的:

区间中的最小数 * 区间所有数的和

小R想知道,经过计算后,哪个区间能产生最大的值。你的任务是帮助小R编写一个程序,输出最大计算值。

例如:给定数组序列 [6, 2, 1],可以得到以下区间及其计算值:

  • [6] = 6 * 6 = 36
  • [2] = 2 * 2 = 4
  • [1] = 1 * 1 = 1
  • [6, 2] = 2 * 8 = 16
  • [2, 1] = 1 * 3 = 3
  • [6, 2, 1] = 1 * 9 = 9

根据这些计算,小R可以选定区间 [6],因此输出的最大值为 36

贪心思想的应用

在这个问题中,贪心思想的核心是:在每一步选择中,尽量选择当前最有利的区间。具体来说,我们可以从每个元素开始,尝试扩展区间,直到不能再扩展为止,然后计算当前区间的乘积,并更新最大值。

算法步骤

  1. 遍历每个元素:将每个元素作为区间的最小值。
  2. 扩展区间:从当前元素开始,向左和向右扩展区间,直到遇到比当前元素更小的数为止。
  3. 计算乘积:计算当前区间的乘积(最小数 * 区间和)。
  4. 更新最大值:在遍历过程中,记录并更新最大值。

实现代码

    public static int solution(int n, int[] a) {
        // 计算前缀和
        int[] prefixSum = new int[n + 1];
        prefixSum[0] = 0;
        for (int i = 0; i < n; i++) {
            prefixSum[i+1] = prefixSum[i] + a[i];
        }

        int ans = Integer.MIN_VALUE;
        for (int i = 0; i < n; i++) {
            int min = a[i];
            int left = i, right = i;
            while (left > 0 && a[left - 1] >= min) {
                left--;
            }
            while (right < n - 1 && a[right + 1] >= min) {
                right++;
            }

            int sum = prefixSum[right + 1] - prefixSum[left];
            int value = min * sum;
            ans = Math.max(ans, value);
        }
        return ans;
    }

关键步骤

  • 遍历每个元素for (int i = 0; i < n; i++)
  • 扩展区间while (left > 0 && a[left - 1] >= minValue) { left--; } 和 while (right < n - 1 && a[right + 1] >= minValue) { right++; }
  • 计算区间和int sum = 0; for (int j = left; j <= right; j++) { sum += a[j]; }
  • 更新最大值if (product > maxProduct) { maxProduct = product; }

贪心思想的体现

在每一步选择中,我们都尽量选择当前最有利的区间。具体来说,我们选择当前元素作为区间的最小值,并尽可能扩展区间,直到不能再扩展为止。这种选择方式体现了贪心思想的核心:在每一步选择中,都采取当前最有利的选择。

前缀和(Prefix Sum)是一种常用的预处理技术,用于快速计算数组中任意子数组的和。通过预先计算出数组的前缀和,可以在常数时间内回答关于子数组和的查询问题。

前缀和的定义

对于一个数组 A,其前缀和数组 P 定义如下:

  • P[0] = A[0]
  • P[i] = A[0] + A[1] + ... + A[i] 对于 i > 0

换句话说,P[i] 表示从数组 A 的第一个元素到第 i 个元素的和。

计算前缀和

假设我们有一个数组 A,长度为 n,我们可以用以下步骤计算前缀和数组 P

  1. 初始化 P[0] = A[0]
  2. 对于 i 从 1 到 n-1,计算 P[i] = P[i-1] + A[i]

例子

假设我们有数组 A = [3, 1, 4, 2, 5],其前缀和数组 P 为:

  • P[0] = A[0] = 3
  • P[1] = A[0] + A[1] = 3 + 1 = 4
  • P[2] = A[0] + A[1] + A[2] = 3 + 1 + 4 = 8
  • P[3] = A[0] + A[1] + A[2] + A[3] = 3 + 1 + 4 + 2 = 10
  • P[4] = A[0] + A[1] + A[2] + A[3] + A[4] = 3 + 1 + 4 + 2 + 5 = 15

所以,前缀和数组 P[3, 4, 8, 10, 15]

查询子数组和

有了前缀和数组 P,我们可以在常数时间内计算任意子数组 A[i...j] 的和。具体公式如下:

  • 子数组 A[i...j] 的和 = P[j] - P[i-1] (注意:这里的索引是从 0 开始的)

例如,要计算子数组 A[1...3] 的和,即 A[1] + A[2] + A[3],可以使用前缀和数组 P 来计算:

  • P[3] - P[0] = 10 - 3 = 7