难度中等
一个数组的 最小乘积 定义为这个数组中 最小值 乘以 数组的 和 。
- 比方说,数组
[3,2,5]
(最小值是2
)的最小乘积为2 * (3+2+5) = 2 * 10 = 20
。
给你一个正整数数组 nums
,请你返回 nums
任意 非空子数组 的最小乘积 的 最大值 。由于答案可能很大,请你返回答案对 109 + 7
取余 的结果。
请注意,最小乘积的最大值考虑的是取余操作 之前 的结果。题目保证最小乘积的最大值在 不取余 的情况下可以用 64 位有符号整数 保存。
子数组 定义为一个数组的 连续 部分。
示例 1:
输入:nums = [1,2,3,2]
输出:14
解释:最小乘积的最大值由子数组 [2,3,2] (最小值是 2)得到。
2 * (2+3+2) = 2 * 7 = 14 。
示例 2:
输入:nums = [2,3,3,1,2]
输出:18
解释:最小乘积的最大值由子数组 [3,3] (最小值是 3)得到。
3 * (3+3) = 3 * 6 = 18 。
示例 3:
输入:nums = [3,1,5,6,4,2]
输出:60
解释:最小乘积的最大值由子数组 [5,6,4] (最小值是 4)得到。
4 * (5+6+4) = 4 * 15 = 60 。
提示:
1 <= nums.length <= 105
1 <= nums[i] <= 107
# 贡献法 + 单调栈
子数组若最小值相等,和肯定要越大越好
可以枚举最小值,计算出它是哪些子数组的最小值
当前下标 ,往左边寻找第一个小于 的元素下标 ,往右边寻找第一个小于等于或者小于 的元素下标
为什么小于等于或者小于都可以?
例如:
小于:
- 此时 的子数组:
- 此时 的子数组:
一样的
主要在于小于等于:
- 此时 的子数组:
- 此时 的子数组:
不影响最终的结果
class Solution { | |
int MOD = 1000000007; | |
public int maxSumMinProduct(int[] nums) { | |
// 子数组若最小值相等,和肯定要越大越好 | |
// 当前下标 i, | |
// - 需要往左边寻找第一个大于 nums [i] 的元素下标 l | |
// - 需要往右边寻找第一个大于等于 nums [i] 的元素下标 r | |
// 此时对答案的贡献值为 nums [i] * sum (l, r) | |
int n = nums.length; | |
// 前缀和 | |
long[] s = new long[n + 1]; | |
for (int i = 0; i < n; i++) { | |
s[i + 1] = s[i] + nums[i]; | |
} | |
// 存放的是递增的元素 | |
Deque<Integer> st = new LinkedList<>(); | |
st.addFirst(-1); | |
long ans = 0; | |
for (int r = 0; r <= n; r++) { | |
// 说明还有剩余元素,此时内部都是递增的,其右边界为 n,需要都满足条件 | |
int x = r == n ? -1 : nums[r]; | |
while (st.size() > 1 && nums[st.peekFirst()] >= x) { | |
// 此时弹出的栈顶元素 poll 右边第一个小于等于 poll 的元素为 r | |
Integer i = st.pollFirst(); | |
// 此时栈顶弹出的 poll 下面的元素正好是 poll 的左边界 | |
int l = st.peekFirst(); | |
System.out.println("l:" + l + " i:" + i + " r:" + r); | |
ans = Math.max(ans, nums[i] * (s[r] - s[l + 1])); | |
} | |
st.addFirst(r); | |
} | |
return (int) (ans % MOD); | |
} | |
} |
贡献法单调栈