力扣第 240 场周赛 5752. 子数组最小乘积的最大值(前缀和+单调栈)

原题链接

5752. 子数组最小乘积的最大值

题目大意:

给一个数组,求所有子数组的权值最大值,权值的计算方式:子数组中最小的元素乘子数组所有元素的和。

思路

一个单调栈模型的应用。如果枚举所有子数组的话那一定会超时,所以我们只能枚举每一个数,将这个数作为子数组中最小的元素,然后为了使权值最大,就得使元素的和最大,而每个元素都是正数,所以元素的数量要最大。因此对于第i个数作为最小值时,往左找最近的小于他的数,再往右找最近的小于他的数,这个区间内的数组就是满足i作为最小值的最大权值数组,然后计算每个i的数组的权值,求max即可。
看到这个往左找最近的比他小的数,就想到了单调栈。直接用单调栈模型来求即可。求子数组的和可以直接用前缀和。

ac代码:

class Solution {
public:
    int maxSumMinProduct(vector<int>& nums) {
        const int N = 100010;
        typedef long long LL;
        stack<int> q;	//单调栈
        int n = 0,h[N],l[N],r[N];	//h是数组的值,l是往左找最近的小于h[i]的那个数的位置,r是往右找
        LL res = 0,sum[N];	//sum前缀和
        for(auto i : nums)
            h[++n] = i,sum[n] = sum[n-1]+i;	//把num数组转存到h来,sum求前缀和
        h[0] = h[n+1] = 0;	//处理边界
        q.push(0);
        for(int i = 1;i <= n;i++)	//单调栈模板
        {
            while(h[q.top()] >= h[i])
                q.pop();
            l[i] = q.top();
            q.push(i);
        }
        while(q.size())		//清空栈
            q.pop();
        q.push(n+1);
        for(int i = n;i >= 1;i--)
        {
            while(h[q.top()] >= h[i])
                q.pop();
            r[i] = q.top();
            q.push(i);
        }
        for(int i = 1;i <= n;i++)
            res = max(res,(LL)h[i]*(sum[r[i]-1]-sum[l[i]]));	//计算答案
        return res%1000000007;
    }
};

你可能感兴趣的:(每年一题,算法,栈,leetcode,单调栈,前缀和)