美丽塔 I

标签: 数组 单调栈

难度: 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 <= 103
  • 1 <= maxHeights[i] <= 109

Submission

运行时间: 41 ms

内存: 16.4 MB

class Solution:
    def maximumSumOfHeights(self, maxHeights: List[int]) -> int:
        max_num = 0

        for i in range(len(maxHeights)):
            top_num = maxHeights[i]
            this_num = top_num

            for n_iter in (reversed(maxHeights[:i]), maxHeights[i+1:]):
                last_num = top_num
                for n in n_iter:
                    if n <= last_num:
                        this_num += n
                        last_num = n
                    else:
                        this_num += last_num

            if this_num > max_num:
                max_num = this_num

        return max_num

Explain

此题解尝试寻找最大的可能的山脉数组`heights`的高度总和。对于每个可能的山脉顶点`i`,它首先将`heights[i]`设置为`maxHeights[i]`的最大值,并且从顶点向两侧扩展。向左和向右扩展时,确保山脉的高度不增加,即对于左侧的任何`j < i`,有`heights[j] <= heights[j + 1]`,对于右侧的任何`j > i`,有`heights[j] <= heights[j - 1]`。通过遍历每个元素,尝试将其作为山脉顶点,并计算可能的最大高度和。如果当前构造的山脉高度和超过之前记录的最大值,则更新最大值。

时间复杂度: O(n^2)

空间复杂度: O(1)

class Solution:
    def maximumSumOfHeights(self, maxHeights: List[int]) -> int:
        max_num = 0  # 记录所有尝试中的最大高度总和

        for i in range(len(maxHeights)):
            top_num = maxHeights[i]  # 将当前点作为顶点,其高度为该点的最大可能高度
            this_num = top_num  # 初始化当前构建的山脉的高度总和

            # 分别向左和向右构建山脉
            for n_iter in (reversed(maxHeights[:i]), maxHeights[i+1:]):
                last_num = top_num  # 从顶点开始,初始化上一个高度
                for n in n_iter:
                    if n <= last_num:
                        this_num += n  # 如果当前高度可以维持山脉,加到总和中
                        last_num = n  # 更新上一个高度
                    else:
                        this_num += last_num  # 如果当前高度过高,使用上一个高度

            # 更新最大高度总和
            if this_num > max_num:
                max_num = this_num

        return max_num

Explore

算法通过从山顶开始,向左右两侧逐步构建山脉,确保高度不增加。对于每个方向,算法始终尝试将当前高度与前一个高度(从山顶开始)进行比较。如果当前高度小于或等于前一个高度,则当前高度被添加到总和中,这样确保能构建一个符合山脉形状的数组。如果当前高度大于前一个高度,则使用前一个高度的值,这样可以避免山脉的高度突然增加,保持了山脉的递减性质。这种策略确保了即使在高度较低的塔存在时也能获得可能的最大山脉总和。

如果`maxHeights`数组中存在连续的极小值,这些值会限制山脉高度的增加,特别是在这些极小值区域的山脉顶部附近。算法会通过使用这些极小值作为高度,向左右扩展时维持高度不超过这些值。这可能导致在这些区域的山脉总和不是最大可能值。然而,算法通过尝试不同的山脉顶点位置来找到可能的最大总和,这意味着在某些情况下,尽管存在极小值区域,但选择其他位置作为山顶可能会产生更大的总和。

选择`maxHeights[i]`作为山顶是因为这提供了从每个可能的山顶位置获取最大高度的直接方式,从而最大化山脉总和的可能性。从更低的值开始递增虽然理论上可以探索更多可能的山脉形状,但实际上这会大大增加算法的复杂性和运行时间,因为需要对每个位置考虑多种高度。直接使用`maxHeights[i]`作为山顶简化了问题,使得算法只需要关注如何有效地从这一最高点向两侧扩展,以构建符合条件的最大山脉总和。