• 力扣 1856. 子数组最小乘积的最大值


    题目来源:https://leetcode.cn/problems/maximum-subarray-min-product/

    大致题意:
    给一个只包含正数的数组,求所有子数组的最小值与子数组和乘积的最大值


    思路

    根据题意,首先需要求子数组的最小值与子数组和乘积,才能在所有乘积中找到最大值

    要想求出子数组的最小值与子数组和乘积,需要先确定子数组的最小值

    而要确定子数组的最小值,需要先确定子数组的内容,简单的方法是直接枚举所有子数组,然后确定最小值,但是这样的时间复杂度为 O(n2)。还有一种逆向思维的方法

    • 即确定以数组每个元素为最小值的子数组左右边界 l 和 r,可以通过单调栈求出边界
    • 也就是确定以 nums[i] 为最小值的子数组左右边界,那么可知,在边界内包含 nums[i] 的所有子数组的最小值都为 nums[i],而这些子数组与 nums[i] 的最大乘积即为 l ~ r 的所有元素和与 nums[i] 的乘积(因为数组所有元素都为正数)

    于是就确定了子数组的最小值与子数组和乘积,然后可以通过枚举每个元素作为最小值的子数组与对应子数组和的乘积求出所有子数组的最小值与子数组和乘积的最大值

    在解题时,可以通过前缀和来确定给定范围的子数组和

    具体的解题思路为

    1. 使用单调栈求出每个元素作为最小值的子数组的左右边界
    2. 统计前缀和
    3. 根据左边界和前缀和获取对应的子数组和,子数组和与当前元素的乘积即为当前元素为最小值的子数组的最小值与子数组和乘积的最大值。于是,统计最大值(此最大值表示当前元素作为最小值的所有子数组中子数组的最小值与子数组和乘积的最大值)中的最大值(该最大值即为所有子数组的最小值与子数组和乘积的最大值),即为答案

    代码:

    public int maxSumMinProduct(int[] nums) {
            int n = nums.length;
            // 每个元素作为最小值的子数组左边界
            int[] left = new int[n];
            // 每个元素作为最小值的子数组右边界
            int[] right = new int[n];
            // 单调栈
            Deque<Integer> stack = new ArrayDeque<>();
            // 获取左边界
            for (int i = 0; i < n; i++) {
                while (!stack.isEmpty() && nums[stack.peek()] >= nums[i]) {
                    stack.pop();
                }
                left[i] = stack.isEmpty() ? -1 : stack.peek();
                stack.push(i);
            }
            stack.clear();
            // 获取右边界
            for (int i = n - 1; i >= 0; i--) {
                while (!stack.isEmpty() && nums[stack.peek()] > nums[i]) {
                    stack.pop();
                }
                right[i] = stack.isEmpty() ? n : stack.peek();
                stack.push(i);
            }
            long ans = 0;
            // 前缀和
            long[] preSum = new long[n];
            preSum[0] = nums[0];
            // 统计前缀和
            for (int i = 1; i < n; i++) {
                preSum[i] = preSum[i - 1] + nums[i];
            }
            // 统计最大值
            for (int i = 0; i < n; i++) {
                // cur 即为当前元素作为最小值的子数组和的最大值
                long cur = preSum[right[i] - 1];
                if (left[i] != -1) {
                    cur -= preSum[left[i]];
                }
                // cur * nums[i] 即为 当前元素为最小值 的 【子数组的最小值与子数组和乘积】 的 最大值
                ans = Math.max(cur * nums[i], ans);
            }
            ans %= 1000000007;
            return (int) ans;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
  • 相关阅读:
    Java单链表
    独创变频杀毒(血影内核) 瑞星杀毒软件V16保驾护航
    eNSP学习——静态路由及默认路由基本配置
    CMake中list的使用
    微信小程序之微信授权登入及授权的流程讲解
    华为鸿蒙3.0的野望:技术、应用、生态
    电子战基本概念 (01)
    systemd 强大的初始化系统和服务管理器
    GPT实战系列-搭建LangChain流程简单应用
    二十四节气—立秋,文案、海报分享。
  • 原文地址:https://blog.csdn.net/csdn_muxin/article/details/124923413