问题描述
小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。
贪心思想的应用
在这个问题中,贪心思想的核心是:在每一步选择中,尽量选择当前最有利的区间。具体来说,我们可以从每个元素开始,尝试扩展区间,直到不能再扩展为止,然后计算当前区间的乘积,并更新最大值。
算法步骤
- 遍历每个元素:将每个元素作为区间的最小值。
- 扩展区间:从当前元素开始,向左和向右扩展区间,直到遇到比当前元素更小的数为止。
- 计算乘积:计算当前区间的乘积(最小数 * 区间和)。
- 更新最大值:在遍历过程中,记录并更新最大值。
实现代码
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:
- 初始化
P[0] = A[0]。 - 对于
i从 1 到 n-1,计算P[i] = P[i-1] + A[i]。
例子
假设我们有数组 A = [3, 1, 4, 2, 5],其前缀和数组 P 为:
P[0] = A[0] = 3P[1] = A[0] + A[1] = 3 + 1 = 4P[2] = A[0] + A[1] + A[2] = 3 + 1 + 4 = 8P[3] = A[0] + A[1] + A[2] + A[3] = 3 + 1 + 4 + 2 = 10P[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