单调栈求左右边界
题目描述:
一个数组的 最小乘积 定义为这个数组中 最小值 乘以 数组的 和 。
比方说,数组 [3,2,5] (最小值是 2)的最小乘积为 2 * (3+2+5) = 2 * 10 = 20 。
给你一个正整数数组 nums ,请你返回 nums 任意 非空子数组 的最小乘积 的 最大值 。由于答案可能很大,请你返回答案对 109 + 7 取余 的结果。
请注意,最小乘积的最大值考虑的是取余操作 之前 的结果。题目保证最小乘积的最大值在 不取余 的情况下可以用 64 位有符号整数 保存。
子数组 定义为一个数组的 连续 部分。
思路:我们枚举每一个数作为区间的最小值的情况,求出左右大于它的区间范围,然后用前缀和再挨个计算结果,找出最大值.
如何求左右大于它的区间范围呢?答案是单调栈。
我们维护一个单调递增的栈,每次栈中元素出栈时,则表示它的右边界就是当前遍历位置的左边一个,
在当前位置插入之前,栈中的最上面元素的下一个位置,就是当前遍历元素的左边界.
注意初始化把左边界全部设置为0,右边界全部设置为n-1,因为最后有些元素没有出栈,就没有右边界
时间复杂度:O(n);
AC代码:
class Solution { public: const long long mod=1000000007; int maxSumMinProduct(vector<int>& nums) { stack<int>s;int n=nums.size(); vector<int>right(n,n-1),left(n,0); for(int i=0;i<n;i++){ while(!s.empty()&&nums[s.top()]>=nums[i]){ right[s.top()]=i-1;//栈顶右边界确定 s.pop(); } if(!s.empty()){ left[i]=s.top()+1;//当前元素左边界确定 } s.push(i); } vector<long long>sum(n+1,0); for(int i=1;i<=n;i++){ sum[i]=sum[i-1]+nums[i-1]; } long long ans=0; for(int i=0;i<n;i++){//前缀和枚举 ans=max(ans,(sum[right[i]+1]-sum[left[i]])*nums[i]); } return ans%mod; } };