leetcode
leetcode 1951 ~ 2000
巫师的总力量和

巫师的总力量和

难度:

标签:

题目描述

代码结果

运行时间: 334 ms, 内存: 34.2 MB


/*
 * 思路:
 * 利用Java Stream API来处理数组。通过流的方式生成所有可能的子数组,并计算每个子数组的最小值和总和,
 * 然后将它们的乘积加到总力量值中。为了防止结果溢出,需要对结果取模。
 */
import java.util.*;
import java.util.stream.IntStream;

public class WizardStrengthStream {
    public static int totalStrength(int[] strength) {
        int MOD = 1000000007;
        long total = IntStream.range(0, strength.length)
                .mapToLong(i -> IntStream.range(i, strength.length)
                        .mapToObj(j -> Arrays.stream(Arrays.copyOfRange(strength, i, j + 1)))
                        .flatMapToLong(subArray -> {
                            int minStrength = subArray.min().orElse(0);
                            long sum = subArray.sum();
                            return LongStream.of((long) minStrength * sum);
                        })
                        .sum())
                .reduce(0L, (a, b) -> (a + b) % MOD);
        return (int) total;
    }

    public static void main(String[] args) {
        int[] strength1 = {1, 3, 1, 2};
        int[] strength2 = {5, 4, 6};
        System.out.println(totalStrength(strength1)); // 44
        System.out.println(totalStrength(strength2)); // 213
    }
}

解释

方法:

此题解使用了单调栈来寻找每个元素在数组中作为最小值能扩展到的最远左右边界。首先,通过遍历数组使用单调栈计算每个元素的左右边界,其中左边界表示当前元素左侧第一个比它小的元素的位置,右边界表示当前元素右侧第一个比它小的元素的位置。此外,使用前缀和技术来快速计算任意子数组的和,避免重复计算带来的性能损失。最后,结合前缀和和左右边界,快速计算出每个元素作为最小值时,所有包含它的子数组的总力量值的和,并累加起来得到最终结果。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
为什么单调栈在这道题中是有效的?它如何帮助确定每个元素的左右边界?
单调栈是一种可以维护数组元素顺序和值的单调性的数据结构。在这道题中,使用一个递增的单调栈可以帮助我们有效地找到每个元素左侧和右侧第一个比它小的元素的位置。当遍历到新的元素时,栈从顶部开始检查,如果栈顶元素大于等于当前元素,栈顶元素出栈并更新其右边界为当前元素的索引。这样可以保证每个元素被处理时,栈中所有元素都是单调递增的,从而快速找到边界。
🦆
在计算左右边界时,为什么我们在找到一个更小的元素时就停止并设置边界?这样做的原因是什么?
在计算左右边界的过程中,停止并设置边界的时候是因为我们需要找到每个元素作为最小值可以扩展到的最远范围。一旦在某一方向找到了一个更小的元素,这个元素就定义了当前元素作为最小值的边界,因为在这之外的任何较大范围中,当前元素就不再是最小的了。这样的处理保证了正确地计算每个元素作为最小值时的子数组范围。
🦆
前缀和数组ss是如何构建的,为什么它需要使用两次accumulate函数?
前缀和数组ss是通过两次使用accumulate函数构建的。第一次accumulate计算了数组strength的前缀和,即到当前元素为止的所有元素的累加和。第二次accumulate则是基于第一次的结果,再次计算累加和,这样得到的是数组中任意子数组和的累加值。这种方法可以在常数时间内计算出任意子数组的和,大大提高了效率。
🦆
在解法中,计算每个元素作为最小值时的子数组总力量和的公式是怎样的?具体的数学逻辑是什么?
解法中的数学公式用于计算每个元素作为最小值时的子数组总力量和。公式是:`tot = (i - l + 1) * (ss[r + 2] - ss[i + 1]) - (r - i + 1) * (ss[i + 1] - ss[l])`。这里,`(i - l + 1)` 和 `(r - i + 1)` 分别表示从当前元素i到其左边界l和右边界r的元素数量。`ss[r + 2] - ss[i + 1]` 计算的是从i到r的子数组的和,而 `ss[i + 1] - ss[l]` 计算的是从l到i的子数组的和。整体公式考虑了所有包含当前元素并且当前元素是最小值的子数组,并计算出其总力量和。

相关问题