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

152 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

题目来源:leetcode.cn/problems/ma…

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


思路

可以使用前缀和快速求出子数组和,于是可以通过枚举每个元素作为子数组最小值时的子数组的最小值与子数组和乘积求出最大值

  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;
    }