LeetCode/子数组的最小值之和(子数组范围和)

给定一个整数数组 arr,找到 min(b) 的总和,其中 b 的范围为 arr 的每个(连续)子数组。

1. 单调栈

假如要遍历所有区间,哪怕可以直接获得最小值,时间复杂度也是O(n2
这里我们不逐个找对应区间,而是计算每个值对区间的贡献,可以将时间复杂度降到O(n)
其实也就找遍历时当前值的左边界和右边界,在这个区间内该值为最小值,即可直接计算出,该值会对多少个区间做出贡献
先用单调栈找左右两边下一个更小值的坐标即可

class Solution {
public:
    int sumSubarrayMins(vector<int>& arr) {
        int n = arr.size();
        vector<int> monoStack; //单调栈
        vector<int> left(n), right(n);//左右边界
        for (int i = 0; i < n; i++) {//从左往右找左边更小值
            while (!monoStack.empty() && arr[i] <= arr[monoStack.back()]) {//把更大的值挤出来,相等的视为更大,区间计算的时候不影响
                monoStack.pop_back();
            }
            left[i] = i - (monoStack.empty() ? -1 : monoStack.back()); //左区间长度
            monoStack.emplace_back(i);//放入坐标
        }
        monoStack.clear();
        for (int i = n - 1; i >= 0; i--) { //从右往左找右边更小值
            while (!monoStack.empty() && arr[i] < arr[monoStack.back()]) {
                monoStack.pop_back();
            }
            right[i] = (monoStack.empty() ? n : monoStack.back()) - i; //右区间长度
            monoStack.emplace_back(i);
        }
        long long ans = 0;
        long long mod = 1e9 + 7;
        for (int i = 0; i < n; i++) { //计算每个值的贡献
            ans = (ans + (long long)left[i] * right[i] * arr[i]) % mod; 
        }
        return ans;
    }
};

2. 子数组范围和

这里就是多求一个更大值坐标

class Solution {
public:
    long long subArrayRanges(vector<int>& arr) {
        int n = arr.size();
        vector<int> monoStack; //单调栈
        vector<int> minleft(n), minright(n);//作为最小值的区间边界(这里直接存储区间长度)
        for (int i = 0; i < n; i++) {//从左往右找左边更小值
            while (!monoStack.empty() && arr[i] <= arr[monoStack.back()]) {//把更大的值挤出来,相等的视为更大,区间计算的时候不影响
                monoStack.pop_back();
            }
            minleft[i] = i - (monoStack.empty() ? -1 : monoStack.back()); //左区间长度
            monoStack.emplace_back(i);//放入坐标
        }
        monoStack.clear();
        for (int i = n - 1; i >= 0; i--) { //从右往左找右边更小值
            while (!monoStack.empty() && arr[i] < arr[monoStack.back()]) {
                monoStack.pop_back();
            }
            minright[i] = (monoStack.empty() ? n : monoStack.back()) - i; //右区间长度
            monoStack.emplace_back(i);
        }
        monoStack.clear();
        vector<int> maxleft(n), maxright(n);//作为最小值的区间边界(这里直接存储区间长度)
        for (int i = 0; i < n; i++) {//从左往右找左边更大值
            while (!monoStack.empty() && arr[i] >= arr[monoStack.back()]) {//把更大的值挤出来,相等的视为更大,区间计算的时候不影响
                monoStack.pop_back();
            }
            maxleft[i] = i - (monoStack.empty() ? -1 : monoStack.back()); //左区间长度
            monoStack.emplace_back(i);//放入坐标
        }
        monoStack.clear();
        for (int i = n - 1; i >= 0; i--) { //从右往左找右边更大值
            while (!monoStack.empty() && arr[i] > arr[monoStack.back()]) {
                monoStack.pop_back();
            }
            maxright[i] = (monoStack.empty() ? n : monoStack.back()) - i; //右区间长度
            monoStack.emplace_back(i);
        }
        long long ans = 0;
        for (int i = 0; i < n; i++) { //计算每个值的贡献
            ans = (ans + ((long long)maxleft[i]*maxright[i] - minleft[i] * minright[i]) * arr[i]);
        }
        return ans;
    }
};
posted @ 2023-05-19 17:19  失控D大白兔  阅读(26)  评论(0编辑  收藏  举报