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 * 104
1 <= arr[i] <= 3 * 104
class Solution {
private static final int MOD = 1000000007;
public int sumSubarrayMins(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
int n = arr.length;
long ans = 0;
// 起点
for (int i = 0; i < n; i++) {
int min = arr[i];
// 终点
for (int j = i; j < n; j++) {
min = Math.min(min, arr[j]);
ans = (ans + min) % MOD;
}
}
return (int)ans;
}
}
class Solution {
private static final int MOD = 1000000007;
public int sumSubarrayMins(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
int n = arr.length;
long ans = 0;
// 起点
for (int i = 0; i < n; i++) {
int min = arr[i];
// 终点
for (int j = i; j < n; j++) {
min = Math.min(min, arr[j]);
ans += min;
// 将取余转换为减法
if (ans >= MOD) {
ans -= MOD;
}
}
}
return (int)ans;
}
}
class Solution {
private static final int MOD = 1000000007;
public int sumSubarrayMins(int[] arr) {
// 处理边界情况
if (arr == null || arr.length == 0) {
return 0;
}
int n = arr.length;
// 每个元素辐射范围的左边界
int[] left = new int[n];
// 每个元素辐射范围的右边界
int[] right = new int[n];
Deque stack = new LinkedList<>();
// 第一次循环先找到所有元素的左边界
for (int i = 0; i < n; i++) {
// 向左找第一个小于等于E的元素
while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) {
stack.pop();
}
// 设立一个最左边界-1
if (stack.isEmpty()) {
left[i] = -1;
} else {
left[i] = stack.peek();
}
// 下标入栈,方便同时得到i和A[i]
stack.push(i);
}
// 第二次循环找到所有元素的右边界
stack.clear();
for (int i = n - 1; i >= 0; i--) {
// 向右找第一个小于E的元素
while (!stack.isEmpty() && arr[stack.peek()] >= arr[i]) {
stack.pop();
}
// 设立一个最右边界n
if (stack.isEmpty()) {
right[i] = n;
} else {
right[i] = stack.peek();
}
// 下标入栈,方便同时得到i和A[i]
stack.push(i);
}
// 按照贡献度计算即可
// 注意此处left[i]和right[i]实际上记录的是左边界-1和右边界+1,和上面思路中有些区别,便于计算
long ans = 0;
for (int i = 0; i < n; i++) {
ans = (ans + (long)(i - left[i]) * (right[i] - i) * arr[i]) % MOD;
}
return (int)ans;
}
}
class Solution {
private static final int MOD = 1000000007;
// 重写根据下标取值方法,-1和n返回MIN_VALUE
private int getElement(int[] arr, int n, int i) {
if (i == -1 || i == n) {
return Integer.MIN_VALUE;
}
return arr[i];
}
public int sumSubarrayMins(int[] arr) {
// 处理边界情况
if (arr == null || arr.length == 0) {
return 0;
}
int n = arr.length;
long ans = 0;
Deque stack = new LinkedList<>();
// 将下标-1和n作为两个哨兵元素,它们对应的元素为MIN_VALUE
// -1作为最左边界,n作为最右边界
for (int i = -1; i <= n; i++) {
// 向左寻找第一个小于等于A[i]的元素
while (!stack.isEmpty() && getElement(arr, n, stack.peek()) > getElement(arr, n, i)) {
// A[cur]就是之前思路中的A[i],注意区分和上面代码的区别
// 对于每个出栈元素来说,i就是它们的右边界,而栈顶元素就是左边界
int cur = stack.pop();
// 计算贡献值
ans = (ans + (long)(cur - stack.peek()) * (i - cur) * arr[cur]) % MOD;
}
stack.push(i);
}
return (int)ans;
}
}
本题解部分思路借鉴于单调栈 Python3。
大家如有兴趣可以继续尝试下该题目的一道拓展题2104. 子数组范围和,相信大家会有新的收获!
最后,如本题解对您有帮助,请点个赞支持下,非常感谢!