写在前面,很久没刷leetcode的题,已经没有做编程题的感觉了,这道中等难度的题看题解之后还折腾了一天左右,真的是菜鸡,有个坑,一直没有注意。
题目描述:
给定一个整数数组 A
,找到 min(B)
的总和,其中 B
的范围为 A
的每个(连续)子数组。
由于答案可能很大,因此返回答案模 10^9 + 7
。
思路:首先要将题目转换成对于数组A,数组中的某个元素A[i],求出A[j],使得,A[j+1]、A[j+2]、...、A[i-1] > A[i]【注意关于A[j]等于A[i]能否纳入考虑范围,这个放在后面以一个例子来仔细将,本人所跳的坑就在这,简直要吐血】,同理求出A[k],使得:A[i+1]、...、A[k-1] > A[i]。题目转化为求解数组left[i]表示对于元素A[i]而言,满足上述左侧条件的 j 使得(A[j+1]、A[j+2]、...、A[i-1] > A[i]) ,求解数组right[i]表示对于元素A[i]而言,满足上述右侧条件的 k 使得(A[i+1]、...、A[k-1] > A[i])
举个例子,对于3、1、2、4而言,例如元素1,对于其左侧,都比其大(为方便计数,假设A[-1]与A[A.size()]均为无穷小的数),其left = -1, 其 right = 4
对于求解left与right,这里要引入单调栈,即单调递增栈,left[i]表示的是当前元素A[i],其左侧元素中,第一个比它的小的元素的位置,所以运用单调递增栈可以很好地解决这个问题,因为是递增栈,说明当前元素比栈顶元素要大(假设本应该进行的出栈操作已经完成,栈空时假设栈底还有一个很小的元素,其坐标为 -1 ),那栈顶元素就是想找的第一个比当前元素要小的元素,同理求解right也是类似,这个单调递增栈,可能需要自己好好领悟下,说也不容易说清楚。
关于前面判断栈顶元素是否要出栈的判断,若栈顶元素大于当前元素,栈顶元素肯定要出栈。关于栈顶元素与当前元素相等的判断,应该是求解left与right判断条件中只有一个带上等号即可满足。以下面的这个例子来说明。
数组:71 55 (1) 82 55(2)
如果求解left与right都不带等于号:
那么对于第一个55为最小元素时,其左边元素是71 55, 右边55 82 这样算下来就是2*2=4种情况
分别为55 71 55 55 82 71 55 82
对于第二个55为最小元素时,其左边元素是82 55,右边是 55,这样算下来就是2*1=2种
分别为55 82 55
显然这遗漏了 55 82 55这种情况
如果求解都带等于号:
那么对于第一个55为最小元素时,其左边元素是71 55, 右边55 82 55,这样算下来就是2*3=6种情况
分别为71 55 55(1) 55 82 55 82 55 71 55 82 71 55 82 55
对于第二个55为最小元素时,其左边元素为71 55 82 55,右边元素为55,这样算下来就是4*1=4种情况
分别为71 55 82 55 55 82 55 82 55 55(2)
多计算了71 55 82 55 55 82 55
简单来说:对于数组,.......(a个)x ..........(b个) x ...........(c个)如果不带“=”;总数 = a * b + b * c
如果一侧带"=",总数 = a * b + (a+ b) * c 是合理的
class Solution {
public:
int sumSubarrayMins(vector<int>& A) {
int left[A.size()], right[A.size()];//left[i]表示比小于A[i]的元素的位置(左)
//right[i]表示小于A[i]的元素的位置(右)
stack<int> left_v;//构造递增栈
stack<int> right_v;//逆序构造递增栈
for(int i = 0; i < A.size(); i++)
{//顺序构造递增栈
while(left_v.size() != 0 && A[left_v.top()] >= A[i])
{
left_v.pop();
}
left[i] = left_v.size() == 0 ? -1 : left_v.top();
left_v.push(i);
}
for(int i = A.size() - 1; i >= 0; i--)
{//逆序构造递增栈
while(right_v.size() != 0 && A[i] < A[right_v.top()])
{
right_v.pop();
}
right[i] = right_v.size() == 0 ? A.size() : right_v.top();
right_v.push(i);
}
long long ans = 0;
long long mod = 1e9 + 7;
for(int i = 0; i < A.size(); i++)
{
ans += (long long)A[i] * (i - left[i]) * (right[i] - i);
ans = ans % mod;
}
ans = ans % mod;
return ans;
}
};