原文链接: https://leetcode-cn.com/problems/maximum-subarray-min-product
英文原文
The min-product of an array is equal to the minimum value in the array multiplied by the array's sum.
- For example, the array
[3,2,5]
(minimum value is2
) has a min-product of2 * (3+2+5) = 2 * 10 = 20
.
Given an array of integers nums
, return the maximum min-product of any non-empty subarray of nums
. Since the answer may be large, return it modulo 109 + 7
.
Note that the min-product should be maximized before performing the modulo operation. Testcases are generated such that the maximum min-product without modulo will fit in a 64-bit signed integer.
A subarray is a contiguous part of an array.
Example 1:
Input: nums = [1,2,3,2] Output: 14 Explanation: The maximum min-product is achieved with the subarray [2,3,2] (minimum value is 2). 2 * (2+3+2) = 2 * 7 = 14.
Example 2:
Input: nums = [2,3,3,1,2] Output: 18 Explanation: The maximum min-product is achieved with the subarray [3,3] (minimum value is 3). 3 * (3+3) = 3 * 6 = 18.
Example 3:
Input: nums = [3,1,5,6,4,2] Output: 60 Explanation: The maximum min-product is achieved with the subarray [5,6,4] (minimum value is 4). 4 * (5+6+4) = 4 * 15 = 60.
Constraints:
1 <= nums.length <= 105
1 <= nums[i] <= 107
中文题目
一个数组的 最小乘积 定义为这个数组中 最小值 乘以 数组的 和 。
- 比方说,数组
[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
通过代码
高赞题解
思路:
我们可以考虑枚举数组中每个值n,并且以n作为子数组中的最小值,再乘以这个子数组的和,通过打擂台的方式得到最终答案。于是问题变成,
给定n,如何找到以n为最小值的子数组边界?
朴素的想法是维护左右指针不断扩大范围,但是这种做法总的时间复杂度是O(n^2),会超时,我们需要寻找一种更快的方法。问题可以继续转化成,
对于数组中的每一个元素,分别找到左边、右边第一个比它小的元素的位置
单调栈刚好可以帮助我们在O(n)的时间内完成。维护两个单调递增栈分别找到左右下一个更小的元素位置。再结合前缀和,最后总的时间复杂度为O(n)
代码
class Solution:
def maxSumMinProduct(self, nums: List[int]) -> int:
# 左右添加两个哨兵,方便单调栈内的判断
nums = [0] + nums + [0]
# 前缀和
presum = [0]
for n in nums:
presum.append(presum[-1] + n)
# 右边第一个比它小的元素下标
right_first_smaller = [None] * len(nums)
stack = []
for i in range(len(nums)):
# 如果当前元素比栈顶元素小,弹栈
while stack and nums[i] < nums[stack[-1]]:
right_first_smaller[stack.pop()] = i
stack.append(i)
# 左边第一个比它小的元素下标
left_first_smaller = [None] * len(nums)
stack = []
for i in range(len(nums)-1,-1,-1):
# 如果当前元素比栈顶元素小,弹栈
while stack and nums[i] < nums[stack[-1]]:
left_first_smaller[stack.pop()] = i
stack.append(i)
# 打擂台得到答案
res = 0
for i in range(1,len(nums)-1):
left = left_first_smaller[i]
right = right_first_smaller[i]
res = max(res, nums[i] * (presum[right] - presum[left+1]))
return res % (10 ** 9 + 7)
class Solution {
public:
int maxSumMinProduct(vector<int>& nums) {
// 左右添加两个哨兵,方便单调栈内的判断
nums.insert(nums.begin(), 0);
nums.push_back(0);
// 前缀和
vector<long long> presum = {0};
for(auto& n: nums)
presum.push_back(presum.back() + n);
// 右边第一个比它小的元素下标
stack<int> s;
vector<int> rightFirstSmaller(nums.size(), 0);
for(int i = 0;i < nums.size();i++){
// 如果当前元素比栈顶元素小,弹栈
while(!s.empty() && nums[i] < nums[s.top()]){
int index = s.top();
s.pop();
rightFirstSmaller[index] = i;
}
s.push(i);
}
// 左边第一个比它小的元素下标
s = stack<int>();
vector<int> leftFirstSmaller(nums.size(), 0);
for(int i = nums.size()-1;i >= 0;i--){
// 如果当前元素比栈顶元素小,弹栈
while(!s.empty() && nums[i] < nums[s.top()]){
int index = s.top();
s.pop();
leftFirstSmaller[index] = i;
}
s.push(i);
}
// 打擂台得到答案
long long res = 0;
for(int i = 1;i < nums.size()-1;i++){
int l = leftFirstSmaller[i];
int r = rightFirstSmaller[i];
res = max(res,nums[i] * (presum[r] - presum[l+1]));
}
return res % 1000000007;
}
};
有关单调栈的入门题目:
必会高频题,重点掌握:
统计信息
通过次数 | 提交次数 | AC比率 |
---|---|---|
4704 | 13199 | 35.6% |
提交历史
提交时间 | 提交结果 | 执行时间 | 内存消耗 | 语言 |
---|