持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第12天,点击查看活动详情
907. 子数组的最小值之和
难度:中等
题目:
给定一个整数数组 arr,找到 min(b) 的总和,其中 b 的范围为 arr 的每个(连续)子数组。
由于答案可能很大,因此 返回答案模 10^9 + 7 。
示例 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。
示例 2:
输入: arr = [11,81,94,43,3]
输出: 444
提示:
1 <= arr.length <= 3 * 1041 <= arr[i] <= 3 * 104
个人思路
1.单调栈
如果我们枚举所有子数组,再逐个求最小值,显然是行不通的。应该逆向思考这个问题,即求最小值为某个值的区间个数,由于最小值一定在arr中产生,我们可以遍历每一个值,求出以它为最小值的区间数目。 如何界定呢?方法是找到左右两边最近的更小值的位置,这样,(left,now]都可以作为起点,[now,right)的都可以作为终点,一乘就可以了。
举个例子 对题目的第二个例子进行具体计算。 arr = [11,81,94,43,3]
对于11,左边没有更小值,右边更小值为3,那么,只能以11为起点,终点可以选11,81,94,43,共4个数组,对答案的贡献就是411=44 对于81,左边更小值为11,右边更小值为43,那么,可以以81为起点,81,94为终点,共两个数组,对答案的贡献为281=162 对于94,左边更小值为81,右边更小值为43,只能以自身为起点终点,共一个数组,贡献94 对于43,左边更小值为11,右边更小值为3,可以以81,94,43为起点,43为终点,共三个数组,对答案的贡献为343=129 对于3,左边没有更小值,右边没有更小值,可以以11,81,94,43,3为起点,3为终点,共五个数组,对答案的贡献为53=15 最终结果就是累加起来,44+162+94+129+15=444 更一般来说,如果当前位置坐标为i,左边更小值位置为pos1,右边为pos2,则贡献就是(i-pos1)*(pos2-i)*arr[i]。特别地,如果左边没有更小值,pos1=-1,如果右边没有更小值,pos2=size。
接下来的问题就是,如何快速地求出左右的最近更小值位置,这就要利用单调栈。我们维护一个单调递增栈,里面存放下标(下文的值都是下标对应的值)。对于每一个值,如果栈顶>=当前值,就出栈,直到栈为空或者栈顶更小,此时栈顶就是最近更小值的位置,然后将当前下标入栈。这样,每次可以获取左边更小值的位置。要想获取右边的位置,很直白的做法是倒着来一遍,不过,其实当栈顶>=当前值时,当前下标就是栈顶右边更小值的位置,所以我们只需要在出栈时进行计算就行了,为了全部计算,我们人为地在右边添加一个最小值,保证最后全部出栈计算完毕。
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;
}
};
每天记录一下做题思路。