单调栈求左右边界

传送门

题目描述:

一个数组的 最小乘积 定义为这个数组中 最小值 乘以 数组的 和 。

比方说,数组 [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;
    }
};

 

posted @ 2021-05-11 21:08  cono奇犽哒  阅读(107)  评论(0)    收藏  举报