#Leetcode-907. 子数组的最小值之和(单调栈)

题目

给定一个整数数组 arr,找到 min(b) 的总和,其中 b 的范围为 arr 的每个子数组。
来源:力扣(LeetCode)

示例1:

输入:arr = [3,1,2,4]
输出:17
解释:
子数组为 [3],[1],[2],[4],[3,1],[1,2],[2,4],[3,1,2],[1,2,4],[3,1,2,4]。 
最小值为 3,1,2,4,1,1,2,1,1,1,和为 17

题目可能描述的不是很好,看示例一就很容易理解题意了,就是求所有子数组中的最小数的和。

思路1

刚开始看到这题想到的就是遍历每个数,然后求每个数起作用的范围,有了作用范围也就知道了该数被使用了多少次。也就是对任意第i个数找到左边第一个严格小于它的数和右边第一个大于等于它的数。(这里假设子数组中最小数若存在两个以上,则用最左边的数)

代码如下:

class Solution {
public:
    int sumSubarrayMins(vector<int>& arr) {
        int n = arr.size();
        int mod = 1000000007;
        int l, r;
        long res = 0;
        for (int i = 0; i < n; i++) {
            l = i - 1;
            r = i + 1;
            while (l >= 0 && arr[l] > arr[i]) l--;
            while (r < n && arr[r] >= arr[i]) r++;
            res += (r - i) * (i - l) * arr[i] % mod;
        }
        return (int)(res% mod);
    }
};

很容易分析,复杂度为o(n^2),然后就超时了。也很自然的想到问题是出在找作用范围这部分,因为对于每个数我们找它的作用范围都是o(n)复杂度,这里肯定是可以优化的。

思路2

重新分析该问题,我们的目标是找到长度为n的数组的子数组的某个和,所有的子数组可以以这样一个角度去看待,即:以从第0到(n-1)为结尾的子数组之和。
这样对于以第 i 个数为结尾的所有子数组,它的最小数的和可以自然的转化为以第i个数为最小数的子数组和其他子数组,这里的其他子数组由于我们只求该子数组的最小值就可以转化为以第k(k 所以,这就是一个动态规划问题。
我们以数学语言表达这个问题:
记: dp[i] 为以i为结尾且的所有子数组的最小值之和,k为i左边第一个比它小的数的位置。
则dp[i] = (子数组中以第i个数为最小数的子数组个数)*arr[i] + dp[k]。

后面一个问题就是如何快速求这个k,那么就得用到 单调栈 \bold{单调栈} 单调栈这个东西了,原理也很简单,以递减栈为例,当栈非空时候,如果当前数比栈顶数小,则把栈顶元素弹出,否者入栈。这里为了快速找到是第几个元素,因此栈内存储的是元素的序号,比较用arr取值。

代码如下:

class Solution {
public:
    int sumSubarrayMins(vector<int>& arr) {
        int n = arr.size();
        int mod = 1000000007;
        vector<int> dp(n);
        stack<int> st;
        for (int i = 0; i < n; i++) {
            while (!st.empty() && arr[i] <= arr[st.top()]) {
                st.pop();
            }
            int preId = st.empty() ? -1 : st.top();
            st.push(i);
            if (preId == -1) {
                dp[i] = arr[i] * (i + 1);
            }
            else {
                dp[i] = arr[i] * (i - preId) + dp[preId];
            }
        }
        int res = 0;
        for (int i = 0; i < n; i++) {
            res = (res + dp[i]) % mod;
        }
        return res;
    }
};

你可能感兴趣的:(Leetcode刷题,c++,leetcode)