思路: 理解一个概念,当最小值确定后,连续子数组要尽可能地长,得到的是指定连续数组最小值后最小乘积的最大值。首先求出数组的前缀和数组,用于计算任意两个索引之间元素的和,然后假设数组中的每一个元素都可以作为连续子数组中的最小值,维护left和right两个数组,分别存放左边比当前元素值小的第一个元素索引,右边比当前元素值小的第一个元素索引,该过程使用单调栈求解,最后以原数组中每个元素为最小值,通过left数组和right数组求得连续数组的最小乘积,取结果最大的即可。
class Solution {
public int maxSumMinProduct(int[] nums) {
int n=nums.length;
long[] presum=new long[n+1];
presum[0]=0;
//前缀和数组
for(int i=1;i<=n;i++){
presum[i]=presum[i-1]+nums[i-1];
}
//使用单调栈求左、右第一个小于当前元素值的元素的索引。
Deque<Integer> stack1=new LinkedList<>();
Deque<Integer> stack2=new LinkedList<>();
int[] left=new int[n];
int[] right=new int[n];
for(int i=0;i<n;i++){
//弹出左边所有大于当前元素值的元素索引
while(!stack1.isEmpty()&&nums[i]<=nums[stack1.peek()]){
stack1.pop();
}
//栈为空,说明当前元素是所有左边元素的最小值,连续子数组的左边界设为-1
if(stack1.isEmpty()){
left[i]=-1;
}else{
//栈不为空,连续子数组的左边界为栈顶元素,即第一个比当前元素值小的元素的索引
left[i]=stack1.peek();
}
//当前元素的索引进栈
stack1.push(i);
}
//同上,找比当前元素值小的第一个右边元素
for(int i=n-1;i>=0;i--){
while(!stack2.isEmpty()&&nums[i]<=nums[stack2.peek()]){
stack2.pop();
}
if(stack2.isEmpty()){
right[i]=n;
}else{
right[i]=stack2.peek();
}
stack2.push(i);
}
long res=0;
for(int i=0;i<n;i++){
//以当前元素为最小值的连续子数组的最小乘积
long sum=nums[i]*(presum[right[i]]-presum[left[i]==-1?0:left[i]+1]);
res=Math.max(res,sum);
}
return (int)(res%1000000007);
}
}