题目来源:https://leetcode.cn/problems/maximum-subarray-min-product/
大致题意:
给一个只包含正数的数组,求所有子数组的最小值与子数组和乘积的最大值
根据题意,首先需要求子数组的最小值与子数组和乘积,才能在所有乘积中找到最大值
要想求出子数组的最小值与子数组和乘积,需要先确定子数组的最小值
而要确定子数组的最小值,需要先确定子数组的内容,简单的方法是直接枚举所有子数组,然后确定最小值,但是这样的时间复杂度为 O(n2)。还有一种逆向思维的方法
于是就确定了子数组的最小值与子数组和乘积,然后可以通过枚举每个元素作为最小值的子数组与对应子数组和的乘积求出所有子数组的最小值与子数组和乘积的最大值
在解题时,可以通过前缀和来确定给定范围的子数组和
具体的解题思路为
代码:
public int maxSumMinProduct(int[] nums) {
int n = nums.length;
// 每个元素作为最小值的子数组左边界
int[] left = new int[n];
// 每个元素作为最小值的子数组右边界
int[] right = new int[n];
// 单调栈
Deque<Integer> stack = new ArrayDeque<>();
// 获取左边界
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && nums[stack.peek()] >= nums[i]) {
stack.pop();
}
left[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(i);
}
stack.clear();
// 获取右边界
for (int i = n - 1; i >= 0; i--) {
while (!stack.isEmpty() && nums[stack.peek()] > nums[i]) {
stack.pop();
}
right[i] = stack.isEmpty() ? n : stack.peek();
stack.push(i);
}
long ans = 0;
// 前缀和
long[] preSum = new long[n];
preSum[0] = nums[0];
// 统计前缀和
for (int i = 1; i < n; i++) {
preSum[i] = preSum[i - 1] + nums[i];
}
// 统计最大值
for (int i = 0; i < n; i++) {
// cur 即为当前元素作为最小值的子数组和的最大值
long cur = preSum[right[i] - 1];
if (left[i] != -1) {
cur -= preSum[left[i]];
}
// cur * nums[i] 即为 当前元素为最小值 的 【子数组的最小值与子数组和乘积】 的 最大值
ans = Math.max(cur * nums[i], ans);
}
ans %= 1000000007;
return (int) ans;
}