【每日一题】907. 子数组的最小值之和-2023.11.27

题目:

907. 子数组的最小值之和

给定一个整数数组 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

提示:

  • 1 <= arr.length <= 3 * 104
  • 1 <= arr[i] <= 3 * 104

解答: 

【每日一题】907. 子数组的最小值之和-2023.11.27_第1张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第2张图片 【每日一题】907. 子数组的最小值之和-2023.11.27_第3张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第4张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第5张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第6张图片 

【每日一题】907. 子数组的最小值之和-2023.11.27_第7张图片 

【每日一题】907. 子数组的最小值之和-2023.11.27_第8张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第9张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第10张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第11张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第12张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第13张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第14张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第15张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第16张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第17张图片

代码1: 

class Solution {
    private static final int MOD = 1000000007;
    public int sumSubarrayMins(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        int n = arr.length;
        long ans = 0;
        // 起点
        for (int i = 0; i < n; i++) {
            int min = arr[i];
            // 终点
            for (int j = i; j < n; j++) {
                min = Math.min(min, arr[j]);
                ans = (ans + min) % MOD;
            }
        }
        return (int)ans;
    }
}
class Solution {
    private static final int MOD = 1000000007;
    public int sumSubarrayMins(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        int n = arr.length;
        long ans = 0;
        // 起点
        for (int i = 0; i < n; i++) {
            int min = arr[i];
            // 终点
            for (int j = i; j < n; j++) {
                min = Math.min(min, arr[j]);
                ans += min;
                // 将取余转换为减法
                if (ans >= MOD) {
                    ans -= MOD;
                }
            }
        }
        return (int)ans;
    }
}

【每日一题】907. 子数组的最小值之和-2023.11.27_第18张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第19张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第20张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第21张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第22张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第23张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第24张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第25张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第26张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第27张图片

 【每日一题】907. 子数组的最小值之和-2023.11.27_第28张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第29张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第30张图片

【每日一题】907. 子数组的最小值之和-2023.11.27_第31张图片

代码2: 

class Solution {
    private static final int MOD = 1000000007;
    public int sumSubarrayMins(int[] arr) {
        // 处理边界情况
        if (arr == null || arr.length == 0) {
            return 0;
        }
        int n = arr.length;
        // 每个元素辐射范围的左边界
        int[] left = new int[n];
        // 每个元素辐射范围的右边界
        int[] right = new int[n];
        Deque stack = new LinkedList<>();

        // 第一次循环先找到所有元素的左边界
        for (int i = 0; i < n; i++) {
            // 向左找第一个小于等于E的元素
            while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) {
                stack.pop();
            }
            // 设立一个最左边界-1
            if (stack.isEmpty()) {
                left[i] = -1;
            } else {
                left[i] = stack.peek();
            }
            // 下标入栈,方便同时得到i和A[i]
            stack.push(i);
        }

        // 第二次循环找到所有元素的右边界
        stack.clear();
        for (int i = n - 1; i >= 0; i--) {
            // 向右找第一个小于E的元素
            while (!stack.isEmpty() && arr[stack.peek()] >= arr[i]) {
                stack.pop();
            }
            // 设立一个最右边界n
            if (stack.isEmpty()) {
                right[i] = n;
            } else {
                right[i] = stack.peek();
            }
            // 下标入栈,方便同时得到i和A[i]
            stack.push(i);
        }

        // 按照贡献度计算即可
        // 注意此处left[i]和right[i]实际上记录的是左边界-1和右边界+1,和上面思路中有些区别,便于计算
        long ans = 0;
        for (int i = 0; i < n; i++) {
            ans = (ans + (long)(i - left[i]) * (right[i] - i) * arr[i]) % MOD; 
        }
        return (int)ans;
    }
}

【每日一题】907. 子数组的最小值之和-2023.11.27_第32张图片

代码3: 

class Solution {
    private static final int MOD = 1000000007;

    // 重写根据下标取值方法,-1和n返回MIN_VALUE
    private int getElement(int[] arr, int n, int i) {
        if (i == -1 || i == n) {
            return Integer.MIN_VALUE;
        }
        return arr[i];
    }
    
    public int sumSubarrayMins(int[] arr) {
        // 处理边界情况
        if (arr == null || arr.length == 0) {
            return 0;
        }
        int n = arr.length;
        long ans = 0;
        Deque stack = new LinkedList<>();
        // 将下标-1和n作为两个哨兵元素,它们对应的元素为MIN_VALUE
        // -1作为最左边界,n作为最右边界
        for (int i = -1; i <= n; i++) {
            // 向左寻找第一个小于等于A[i]的元素
            while (!stack.isEmpty() && getElement(arr, n, stack.peek()) > getElement(arr, n, i)) {
                // A[cur]就是之前思路中的A[i],注意区分和上面代码的区别
                // 对于每个出栈元素来说,i就是它们的右边界,而栈顶元素就是左边界
                int cur = stack.pop();
                // 计算贡献值
                ans = (ans + (long)(cur - stack.peek()) * (i - cur) * arr[cur]) % MOD;
            }
            stack.push(i);
        }

        return (int)ans;
    }
}

【每日一题】907. 子数组的最小值之和-2023.11.27_第33张图片

本题解部分思路借鉴于单调栈 Python3。

大家如有兴趣可以继续尝试下该题目的一道拓展题2104. 子数组范围和,相信大家会有新的收获!

最后,如本题解对您有帮助,请点个赞支持下,非常感谢!

你可能感兴趣的:(leetcode刷题笔记,算法,数据结构,leetcode)