Leetcode-子数组的最小值之和

写在前面,很久没刷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;
    }
};

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值