第六天子数组的最小值之和

子数组的最小值之和

问题描述:
给定一个整数数组 arr,找到 min(b) 的总和,其中 b 的范围为 arr 的每个(连续)子数组。

由于答案可能很大,因此 返回答案模 10^9 + 7 。

示例 1:

输入:arr = [3,1,2,4]
输出:17
解释:
子数组为 [3],[1],[2],[4],[3,1],[1,2],[2,4],[3,1,2],[1,2,4],[3,1,2,4]。
最小值为 3,1,2,4,1,1,2,1,1,1,和为 17。
示例 2:

输入:arr = [11,81,94,43,3]
输出:444

问题求解:

class Solution {
public:
    const int MOD = 1e9 + 7;
    int sumSubarrayMins(vector<int>& arr) {
        int n = arr.size();
        // 单调栈:求上一个更小元素
        stack<int> stk;
        // dp[i] 表示以 i 结尾的子数组的最小值之和
        vector<int> dp(n);
        for (int i = 0; i < n; i++) {
            while (!stk.empty() && arr[stk.top()] >= arr[i]) {
                stk.pop(); 
            }    
            // 上一个更小元素
            int preIdx = stk.empty() ? -1 : stk.top();
            stk.push(i);
            
            if (preIdx == -1) {
                // 如果没有上一个更小元素,则以 i 结尾的 i + 1 个区间的最小值都为 arr[i]
                dp[i] = arr[i] * (i + 1); 
            } else {
                // 如果存在上一个更小元素,则 [preIdx, i] 之间的所有子区间的最小值都为 arr[i]
                dp[i] = dp[preIdx] + arr[i] * (i - preIdx);
            }
        }
        int res = 0;
        for (int cnt : dp) {
            res = (res + cnt) % MOD;
        }
        return res;
    }
};

问题总结:
老规矩先看一下官方求解。
方法一:单调栈
考虑所有满足以数组 \textit{arr}arr 中的某个元素 \textit{arr}[i]arr[i] 为最右且最小的元素的子序列个数 C[i]C[i],那么题目要求求连续子数组的最小值之和即为 \sum_{i=0}^{n-1} \limits \textit{arr}[i] \times C[i]
i=0

n−1

arr[i]×C[i],其中数组 \textit{arr}arr 的长度为 nn。我们必须假设当前元素为最右边且最小的元素,这样才可以构造互不相交的子序列,否则会出现多次计算,因为一个数组的最小值可能不唯一。

经过以上思考,我们只需要找到每个元素 \textit{arr}[i]arr[i] 以该元素为最右且最小的子序列的数目 \textit{left}[i]left[i],以及以该元素为最左且最小的子序列的数目 \textit{right}[i]right[i],则以 \textit{arr}[i]arr[i] 为最小元素的子序列的数目合计为 \textit{left}[i] \times \textit{right[i]}left[i]×right[i]。当然为了防止重复计算,我们可以设 \textit{arr}[i]arr[i] 左边的元素都必须满足小于等于 \textit{arr}[i]arr[i],\textit{arr}[i]arr[i] 右边的元素必须满足严格小于 \textit{arr}[i]arr[i]。当然这就变成求最小的下标 j \le ij≤i,且连续子序列中的元素 \textit{arr}[j], \textit{arr}[j+1], \cdots, \textit{arr}[i]arr[j],arr[j+1],⋯,arr[i] 都满足大于等于 \textit{arr}[i]arr[i],以及最大的下标 k > ik>i 满足连续子序列 \textit{arr}[i + 1], \textit{arr}[i+1], \cdots, \textit{arr}[k]arr[i+1],arr[i+1],⋯,arr[k] 都满足严格大于 \textit{arr}[i]arr[i]。上述即转化为经典的单调栈问题,即求数组中当前元素 xx 左边第一个小于 xx 的元素以及右边第一个小于等于 xx 的元素,关于「单调栈」的算法细节,可以参考「496. 下一个更大元素 I 题解」。

对于数组中每个元素 \textit{arr}[i]arr[i],具体做法如下:

求左边第一个小于 \textit{arr}[i]arr[i] 的元素:从左向右遍历数组,并维护一个单调递增的栈,遍历当前元素 \textit{arr}[i]arr[i],如果遇到当前栈顶的元素大于等于 \textit{arr}[i]arr[i] 则将其弹出,直到栈顶的元素小于 \textit{arr}[i]arr[i],栈顶的元素即为左边第一个小于 \textit{arr}[i]arr[i] 的元素 \textit{arr}[j]arr[j],此时 \textit{left}[i] = i - jleft[i]=i−j。

求右边第一个大于等于 \textit{arr}[i]arr[i] 的元素:从右向左遍历数组,维护一个单调递增的栈,遍历当前元素 \textit{arr}[i]arr[i],如果遇到当前栈顶的元素大于 \textit{arr}[i]arr[i] 则将其弹出,直到栈顶的元素小于等于 \textit{arr}[i]arr[i],栈顶的元素即为右边第一个小于等于 \textit{arr}[i]arr[i] 的元素 \textit{arr}[k]arr[k],此时 \textit{right}[i] = k - iright[i]=k−i。

连续子数组 \textit{arr}[j], \textit{arr}[j + 1], \cdots, \textit{arr}[k]arr[j],arr[j+1],⋯,arr[k] 的最小元素即为 \textit{arr}[i]arr[i],以 \textit{arr}[i]arr[i] 为最小元素的连续子序列的数量为 (i - j) \times (k - i)(i−j)×(k−i)。

根据以上结论可以知道,所有子数组的最小值之和即为 \sum_{i=0}^{n - 1} \limits \textit{arr}[i] \times \textit{left}[i] \times \textit{right}[i]
i=0

n−1

arr[i]×left[i]×right[i]。维护单调栈的过程线性的,因为只进行了线性次的入栈和出栈。

作者:LeetCode-Solution
链接:https://leetcode.cn/problems/sum-of-subarray-minimums/solution/zi-shu-zu-de-zui-xiao-zhi-zhi-he-by-leet-bp3k/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ValueError at /myApp/address/ Only supported for TrueType fonts Request Method: GET Request URL: http://127.0.0.1:8000/myApp/address/ Django Version: 2.2.28 Exception Type: ValueError Exception Value: Only supported for TrueType fonts Exception Location: C:\Users\天子墨\AppData\Roaming\Python\Python39\site-packages\PIL\ImageDraw.py in textbbox, line 671 Python Executable: C:\Users\天子墨\AppData\Local\Programs\Python\Python39\python.exe Python Version: 3.9.5 Python Path: ['C:\\Users\\天子墨\\Desktop\\毕业设计\\大学生就业形势(boss)数据可视化分析完整版代码\\boss直聘数据可视化分析', 'C:\\Users\\天子墨\\Desktop\\毕业设计\\大学生就业形势(boss)数据可视化分析完整版代码', 'C:\\Users\\天子墨\\AppData\\Local\\Programs\\Python\\Python39\\python39.zip', 'C:\\Users\\天子墨\\AppData\\Local\\Programs\\Python\\Python39\\DLLs', 'C:\\Users\\天子墨\\AppData\\Local\\Programs\\Python\\Python39\\lib', 'C:\\Users\\天子墨\\AppData\\Local\\Programs\\Python\\Python39', 'C:\\Users\\天子墨\\AppData\\Roaming\\Python\\Python39\\site-packages', 'C:\\Users\\天子墨\\AppData\\Roaming\\Python\\Python39\\site-packages\\win32', 'C:\\Users\\天子墨\\AppData\\Roaming\\Python\\Python39\\site-packages\\win32\\lib', 'C:\\Users\\天子墨\\AppData\\Roaming\\Python\\Python39\\site-packages\\Pythonwin', 'C:\\Users\\天子墨\\AppData\\Local\\Programs\\Python\\Python39\\lib\\site-packages'] Server time: 星期三, 7 六月 2023 10:33:12 +0800
06-08

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值