问题:
给定一个整数数组 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
代码:
import java.util.Stack;
class Solution {
private static final int inf = 0x3f3f3f3f;
private static final int mod = 1000000007;
//单调栈解法
// public int sumSubarrayMins(int[] arr) {
// long ans = 0;
// int n = arr.length;
// int lft[] = new int[n];
// int rgt[] = new int[n];
// // 维护一个递增的单调栈,存储下标
// Stack<Integer> monoStack = new Stack<Integer>();
// for (int i = 0; i < n; i++) {
// // 找出以arr[i]为右端且为最小值的区间数目
// while(!monoStack.empty() && arr[i] <= arr[monoStack.peek()]){
// monoStack.pop();
// }
// // 若栈空说明整段区间[arr[0], arr[i]]之中arr[i]为最小值,其包含的以arr[i]为右端且为最小值的子区间数目为j + 1
// // 否则整段区间[arr[j], arr[i]]之中arr[i]为最小值,其以arr[i]为右端且为最小值的子区间数目i - j
// rgt[i] = i - (monoStack.empty() ? -1 : monoStack.peek());
// monoStack.push(i);
// }
//
// monoStack.clear();
//
// for (int i = n - 1; i >= 0; i--) {
// // 找出以arr[i]为左端且为最小值的区间数目
// while(!monoStack.empty() && arr[i] < arr[monoStack.peek()]){
// monoStack.pop();
// }
// // 若栈空说明整段区间[arr[i], arr[n-1]]之中arr[i]为最小值,其包含的以arr[i]为左端且为最小值的子区间数目为n - i
// // 否则整段区间[arr[i], arr[j]]之中arr[i]为最小值,其以arr[i]为左端且为最小值的子区间数目j - i
// lft[i] = (monoStack.empty() ? n - i: monoStack.peek() - i);
// monoStack.push(i);
// }
//
//
//
//
// for (int i = 0; i < n; i++) {
// ans = (ans + (long) lft[i] * rgt[i] * arr[i]) % mod;
// }
//
// return (int)ans;
// }
// dp+单调栈
public int sumSubarrayMins(int[] arr){
/*
假设s[j][i]为区间j~i的最小值
则答案应该为所有区间的s[j][i]的和
暴力解会导致空间超限
使用dp计算转移方程
已知以arr[i]为右端的区间为s[0][i]
设以arr[i]为右端最小值的区间长度为k,则区间[i - k + 1][i]的最小值为arr[i]
即s[i - k + 1][i] = arr[i]
剩余区间[0~i - k][i]最小值则必定小于arr[i]且不唯一
应该为s[0][i - k](表示arr[0]~arr[i - k]的最小值)、s[1][i - k]...
所求的arr[i]为右端的所有区间最小值和应该为 k * arr[i] + s[0][i - k] + s[1][i - k]...
所求答案就是每个arr[i]为右端的所有区间最小值和的和
使用单调栈求k,即求i减去第一个小于arr[i]的值的下标
设dp[i]表示为s[0][i] ~ s[i][i]的和即 (i∈[0, n))k * arr[i] + s[0][i - k] + s[1][i - k]...
则dp[i] = k * arr[i] + dp[i - k]
* */
long ans = 0;
int n = arr.length;
int[] dp = new int[n];
Stack<Integer> monoStack = new Stack<Integer>();
for (int i = 0; i < n; i++) {
while(!monoStack.empty() && arr[i] < arr[monoStack.peek()]){
monoStack.pop();
}
int k = i - (monoStack.empty() ? -1 : monoStack.peek());
// k有可能为i但是dp[0]是为arr[i]的,所以需要修改一下
dp[i] = k * arr[i] + (monoStack.empty() ? 0 : dp[i - k]);
ans = (ans + dp[i]) % mod;
monoStack.push(i);
}
return (int) ans;
}
}