美丽塔 II

标签: 数组 单调栈

难度: Medium

给你一个长度为 n 下标从 0 开始的整数数组 maxHeights 。

你的任务是在坐标轴上建 n 座塔。第 i 座塔的下标为 i ,高度为 heights[i] 。

如果以下条件满足,我们称这些塔是 美丽 的:

  1. 1 <= heights[i] <= maxHeights[i]
  2. heights 是一个 山脉 数组。

如果存在下标 i 满足以下条件,那么我们称数组 heights 是一个 山脉 数组:

  • 对于所有 0 < j <= i ,都有 heights[j - 1] <= heights[j]
  • 对于所有 i <= k < n - 1 ,都有 heights[k + 1] <= heights[k]

请你返回满足 美丽塔 要求的方案中,高度和的最大值 。

示例 1:

输入:maxHeights = [5,3,4,1,1]
输出:13
解释:和最大的美丽塔方案为 heights = [5,3,3,1,1] ,这是一个美丽塔方案,因为:
- 1 <= heights[i] <= maxHeights[i]  
- heights 是个山脉数组,峰值在 i = 0 处。
13 是所有美丽塔方案中的最大高度和。

示例 2:

输入:maxHeights = [6,5,3,9,2,7]
输出:22
解释: 和最大的美丽塔方案为 heights = [3,3,3,9,2,2] ,这是一个美丽塔方案,因为:
- 1 <= heights[i] <= maxHeights[i]
- heights 是个山脉数组,峰值在 i = 3 处。
22 是所有美丽塔方案中的最大高度和。

示例 3:

输入:maxHeights = [3,2,5,5,2,3]
输出:18
解释:和最大的美丽塔方案为 heights = [2,2,5,5,2,2] ,这是一个美丽塔方案,因为:
- 1 <= heights[i] <= maxHeights[i]
- heights 是个山脉数组,最大值在 i = 2 处。
注意,在这个方案中,i = 3 也是一个峰值。
18 是所有美丽塔方案中的最大高度和。

提示:

  • 1 <= n == maxHeights <= 105
  • 1 <= maxHeights[i] <= 109

Submission

运行时间: 227 ms

内存: 39.9 MB

class Solution:

    def maximumSumOfHeights(self, maxHeights: List[int]) -> int:
        n = len(maxHeights)
        pre = [0] * n 
        post = [0] * n 
        s = []
        for i in range(n):
            while s and maxHeights[s[-1]] > maxHeights[i]:
                s.pop()
            if not s:
                pre[i] = (i+1)*maxHeights[i]
            else:
                pre[i] = pre[s[-1]]+(i-s[-1])*maxHeights[i]
            s.append(i)
        s = []
        res = 0
        for i in range(n-1, -1, -1):
            while s and maxHeights[s[-1]] > maxHeights[i]:
                s.pop()
            if not s:
                post[i] = (n-i)*maxHeights[i]
            else:
                post[i] = post[s[-1]]+(s[-1]-i)*maxHeights[i]
            s.append(i)
            res = max(res, pre[i]+post[i]-maxHeights[i])
        return res

            

Explain

The given solution tries to find the maximum sum of heights for a mountain array configuration. First, it uses two arrays `pre` and `post` to store the maximum possible sum of heights from the start to the current index (for `pre`) and from the current index to the end (for `post`). The algorithm performs two passes over the `maxHeights` array. In the first pass (forward direction), it computes for each tower the maximum sum of heights from the start up to and including that tower, under the constraint that tower heights must not decrease. It uses a stack to maintain indices of towers such that the heights at these indices are in a non-decreasing order. This helps in efficiently computing the range sum by popping indices from the stack when the current tower height is less than the height at the top index of the stack. In the second pass (backward direction), it similarly computes the maximum sum of heights from each tower to the end of the array. Finally, for each tower, it combines these sums (subtracting the tower's height once because it's added in both `pre` and `post`), and tracks the maximum sum obtained. This maximum sum represents the highest sum of heights for a mountain array configuration.

时间复杂度: O(n)

空间复杂度: O(n)

class Solution:
    def maximumSumOfHeights(self, maxHeights: List[int]) -> int:
        n = len(maxHeights)
        pre = [0] * n  # Array to store max sum from start to i
        post = [0] * n  # Array to store max sum from i to end
        s = []  # Stack to maintain indices with non-decreasing maxHeights
        for i in range(n):
            while s and maxHeights[s[-1]] > maxHeights[i]:
                s.pop()  # Maintain non-decreasing order in stack
            if not s:
                pre[i] = (i+1)*maxHeights[i]  # Calculate sum from start to i if it's the smallest seen so far
            else:
                pre[i] = pre[s[-1]] + (i-s[-1]) * maxHeights[i]  # Calculate sum using previous values
            s.append(i)
        s = []
        res = 0
        for i in range(n-1, -1, -1):
            while s and maxHeights[s[-1]] > maxHeights[i]:
                s.pop()
            if not s:
                post[i] = (n-i) * maxHeights[i]  # Calculate sum from i to end if it's the smallest seen so far
            else:
                post[i] = post[s[-1]] + (s[-1]-i) * maxHeights[i]  # Calculate sum using previous values
            s.append(i)
            res = max(res, pre[i] + post[i] - maxHeights[i])  # Combine pre and post subtracting current height once
        return res

Explore

在题解中,并没有使用双指针技术;而是采用了单调栈来维护一个非递减序列的索引。这种方法适用于需要快速计算区间最大值或相关统计量的场景,以及在本题中,用于高效地计算每个位置的山脉数组可能的最大高度总和。单调栈可以在一次遍历中处理和维护需要的索引顺序,从而使得计算前缀和后缀时更为高效。

使用堆栈(栈)来维护一个非递减序列的索引,是因为栈具有后进先出(LIFO)的特性,这使得我们可以在遍历数组的过程中,方便地添加或移除元素以维持序列的非递减性。在前向遍历中,如果当前元素小于栈顶元素,就需要移除栈顶元素,保证栈内始终是非递减的;这样可以保证在每个位置计算最大高度总和时,都能快速地获取到需要的最小元素。这种方法提高了效率,避免了重复的比较和计算。

当当前索引对应的高度低于栈顶索引对应的高度时,直接将栈顶元素弹出,是因为栈顶的高度已经无法用于构建一个有效的山脉形态(即在这一点之后高度需要递减)。通过弹出更高的栈顶元素,我们能更新栈以保证只包含可以用来维持山脉数组性质的元素。这样做的优势在于可以确保在每个位置的高度计算都符合山脉数组的要求,同时避免无效的高度值影响最终的计算结果,从而提高算法的正确性和效率。