zoukankan      html  css  js  c++  java
  • 907. Sum of Subarray Minimums

    问题:

    给定数组,求所有连续子数组的最小值之和。

    若所得之数太大,求其mod(10^9 + 7)

    Example 1:
    Input: [3,1,2,4]
    Output: 17
    Explanation: Subarrays are [3], [1], [2], [4], [3,1], [1,2], [2,4], [3,1,2], [1,2,4], [3,1,2,4]. 
    Minimums are 3, 1, 2, 4, 1, 1, 2, 1, 1, 1.  Sum is 17.
     
    Note:
    1 <= A.length <= 30000
    1 <= A[i] <= 30000
    

      

    解法1:

    对于数组中的每一个值A[i]

    当它为最小值时,

    有:向左连续包含它,且所有元素>=它,的元素个数left[i];

    有:向右连续包含它,且所有元素>=它,的元素个数right[i];

    那么当它为最小值的,连续子数组的个数=left[i]*right[i]  (因为要包含它,且为连续的子数组)

    那么最小值之和为:A[i]*left[i]*right[i]

    同理遍历所有元素,则可得整个数组的最小值之和

    1         for(int i=0; i<A.size(); i++){
    2             res=(res+ (A[i]*left[i]*right[i]))%mod;
    3         }
    4         return res;

    接下来,需要构建left[i]和right[i]

    我们使用递增stack来求得。

    left[i]  :A[i]以左,所有元素>=A[i]
    right[i]:A[i]以右,所有元素>=A[i]

    递增stack:(最终形态)栈内元素:为从原数组最小值起递增,

    我们可以同时记录:从上一个栈内元素 stack[i-1] 到该元素 stack[i] 为止,数组内元素的个数,

    这些数num一定大于stack[i],即:num>=stack[i]>stack[i-1]

    那么,A[i]的left[i]或者right[i]则=stack[i]+...stack[i-x] (stack[i-x]>=A[i])的累加即可。

    下面代码的 increasingL 代表 stack:

    1         for(int i=0; i<A.size(); i++){
    2             int cout=1;
    3             while(!increasingL.empty() && increasingL.top().first>A[i]){
    4                 cout+=increasingL.top().second;
    5                 increasingL.pop();
    6             }
    7             left[i]=cout;
    8             increasingL.push({A[i],cout});
    9         }

    left从左向右存储:左边所有大于A[i]的元素个数

    right从右向左存储:右边所有大于A[i]的元素个数

    方法一样,只是有一个需要注意的点:

    ⚠️注意:对于数组中存在重复的数字时,如何处理:

    stack的pop时机:一个>= 另一个>

    这样,会使得,left的包含两个相同的数值,right不包含两个相同的数值。

    对于相同的A[i],A[j],一边包含相同数值,一边不包含,相比两边都包含,

    会减去一次重复计算的两个数都包含的情况。详细请参考下面的“1”

    代码参考:

     1 class Solution {
     2 public:
     3     int sumSubarrayMins(vector<int>& A) {
     4         int res=0, mod = (int)1e9 + 7;
     5         vector<int> left(A.size());
     6         vector<int> right(A.size());
     7         stack<pair<int,int>> increasingL,increasingR;
     8         //(num,从最小的数开始,stack上一个num为止>本值(比自己大)的个数cout)
     9         //e.g.[3,2,1,2,1,4,5,2,3,6]
    10         //         ^   ^     ^ ^ ^
    11         // cout:   3   2     3 1 1
    12         // cout([start~1]:>1+自己)=3:[3,2,1]
    13         // cout((1~1]    :>1+自己)=2:[2,1]
    14         // cout((1~2]    :>2+自己)=3:[4,5,2]
    15         // cout((2~3]    :>3+自己)=1:[3]
    16         // cout((3~6]    :>6+自己)=1:[6]
    17         //stack:[{1,cout:3},{1,cout:2},{2,cout:3},{3,cout:1},{6,cout:1}]
    18         for(int i=0; i<A.size(); i++){
    19             int cout=1;
    20             while(!increasingL.empty() && increasingL.top().first>A[i]){
    21         //stack:[{1,cout:3},{1,cout:2},{2,cout:3},{3,cout:1},{6,cout:1}]
    22         //==的话,就不pop,那么会存储两个1
    23                 cout+=increasingL.top().second;
    24                 increasingL.pop();
    25             }
    26             left[i]=cout;
    27             increasingL.push({A[i],cout});
    28         }
    29         for(int i=A.size()-1; i>=0; i--){
    30             int cout=1;
    31             while(!increasingR.empty() && increasingR.top().first>=A[i]){
    32         //e.g.   [3,2,1,2,1,4,5,2,3,6]
    33         //        ^ ^ ^
    34         // cout:  1 1 8
    35         //cout([end~1]:>=1)=8:[1,2,1,4,5,2,3,6]
    36         //cout((1~2]  :>=2)=1:[2]
    37         //cout((2~3]  :>=3)=1:[3]
    38         //stack:[{1,cout:8},{2,cout:1},{3,cout:1}]
    39         //==的话,pop,那么会存储一个1
    40         //left[第一个1]=3[3,2,1],right[第一个1]=8[1,2,1,4,5,2,3,6]
    41                 //包含了另一个1->所得子数组包含另一个1
    42         //left[第二个1]=2[2,1],right[第二个1]=6[1,4,5,2,3,6]
    43                 //不包含另一个1->所得子数组不包含另一个1
    44                 cout+=increasingR.top().second;
    45                 increasingR.pop();
    46             }
    47             right[i]=cout;
    48             increasingR.push({A[i],cout});
    49         }
    50         for(int i=0; i<A.size(); i++){
    51             res=(res+ (A[i]*left[i]*right[i]))%mod;
    52         }
    53         return res;
    54     }
    55 };

    解法2:

    动态规划:

        dp[i+1] :以数字 A[i] 结尾的所有子数组最小值之和。

    动态转移方程:

        dp[i+1]=(dp[index+1]+(i-index)*A[i])%mod;

    解释:

    以数字 A[i] 结尾的所有子数组,分为两个部分:

    1⃣️,A[i]为最小值的所有子数组

        因为要连续:那么找 i 以前的,第一个小于A[i]的数的位置:index,那么有 i-index 个子数组。

        最小值之和= (i-index) * A[i]

         e.g. [3,1,4,5,2],对于A[i]=2(i=4)来说,第一个小于2的数为1(index=1),

               那么有1以后的数[4,5,2]可做成个子数组 i-index = 4-1 =3个数组:[2] [5,2] [4,5,2]

    2⃣️,比A[i]还小的值,做为最小值的所有子数组

        index之前的数,最小值一定是比A[i]小。

        因为要连续,且以A[i]为结束元素,

        那么,dp[index+1]所表示的所有到A[index]为结尾的子数组,

        再拼上A[index+1]~A[i]的所有元素,组成新的子数组。

        这些子数组的最小值< min(A[index+1]~A[i])=A[i] 不变,因此最小值之和仍然=dp[index+1],也不变。

    最后,求所有子数组=Sum(每一个A[i]为结尾的子数组最小值之和)=Sum[dp[i]]

    其中,的实现,仍然同解法1,使用递增stack。这里,我们存入元素的index值。

    每读入一个小于栈顶元素,栈顶出栈,

    直到读入大于栈顶的元素,入栈。

    得到的栈元素之间的【未入栈的元素】皆大于【两个栈元素】。

    代码参考:

     1 class Solution {
     2 public:
     3     int sumSubarrayMins(vector<int>& A) {
     4         int res=0, mod = (int)1e9 + 7;
     5         stack<int> increasingL;//stack.top()栈顶:上一个比自己值小的index
     6         //到自己i为止:i-index=连续比自己大的元素个数=子数组个数
     7         increasingL.push(-1);
     8         int dp[A.size()+1];
     9         dp[0]=0;
    10         //dp[i] 表示以数字 A[i-1] 结尾的所有子数组最小值之和。
    11         //=dp[index]//index之前的各数组的最小值不变,这些数组合并到i为止的元素组成新的子数组。因此和不变
    12         // + 自己为最小的子数组个数:(i-index)*A[i]
    13         //so:dp[i+1]=dp[index+1]+(i-index)*A[i]
    14         for(int i=0; i<A.size(); i++){
    15             int index;
    16             while(increasingL.top()!=-1 && A[increasingL.top()]>=A[i]){
    17                 increasingL.pop();
    18             }
    19             index=increasingL.top();
    20             dp[i+1]=(dp[index+1]+(i-index)*A[i])%mod;
    21             res=(res+dp[i+1])%mod;
    22             increasingL.push(i);
    23         }
    24         return res;
    25     }
    26 };
  • 相关阅读:
    Go使用dlv调试代码
    1660 super安装tensorflow1.15
    SQL Server高级进阶之表分区删除
    SQL Server高级进阶之分区表创建
    SQL Server高级进阶之索引优化查询
    SQL Server高级进阶之索引碎片维护
    C# WinForm通用自动更新器
    获取当前月第一天,当前月最后一天,上个月日期,上个月的第一天
    去除checkbox选择的三种方式
    用SpringBoot实现策略模式
  • 原文地址:https://www.cnblogs.com/habibah-chang/p/12911091.html
Copyright © 2011-2022 走看看