# 2865. 美丽塔 I

### 题目地址(2865. 美丽塔 I - 力扣（LeetCode）)

https://leetcode.cn/problems/beautiful-towers-i/description/

### 题目描述

给你一个长度为 `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]`

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

&#x20;

**示例 1：**

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

**示例 2：**

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

**示例 3：**

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

&#x20;

**提示：**

* `1 <= n == maxHeights <= 103`
* `1 <= maxHeights[i] <= 109`

### 前置知识

* 单调栈

### 公司

* 暂无

### 思路

朴素的思路是枚举山峰。山峰贪心地取 maxHeight\[i]，因为取不到 maxHeight\[i] 的话后面限制更大不会更优。然后向左向右扩展。扩展的时候除了 maxHeight 限制，还多了一个左边（或者右边）山峰的高度限制。因此可以同时维护一变量 min\_v，表示左边（或者右边）山峰的高度，用于限制可以取到的最大值。

直观上来说就是山的高度在扩展的同时不断地下降或者不变，因此我们只需要每次都保证当前的高度都小于等于前面的山峰的高度即可。

```py
ans, n = 0, len(maxHeight)
        for i, x in enumerate(maxHeight):
            y = t = x
            # t 是高度和，y 是 min_v
            for j in range(i - 1, -1, -1):
                y = min(y, maxHeight[j])
                t += y
            y = x
            for j in range(i + 1, n):
                y = min(y, maxHeight[j])
                t += y
            ans = max(ans, t)
        return ans
```

这种做法时间复杂度是 $O(n^2)$，可以通过，这也是为什么这道题分数比较低的原因。

不过这道题还有一种动态规划 + 单调栈的做法。

以向左枚举为例。同样枚举山峰 i，i 取 maxheight\[i]， 然后找左侧第一个小于它的位置 l（用单调栈）。那么 \[l+1, i-1] 之间的位置都能且最多取到 maxHeight\[l]。那么 \[0, l] 之间的能取到多少呢？这其实相当于以 l 为峰顶左侧的最大和。这不就是一个规模更小的子问题吗？用动态规划即可。

向右也是同理，不再赘述。

### 关键点

* 单调栈优化
* 动态规划

### 代码

* 语言支持：Python3

Python3 Code:

```python

class Solution:
    def maximumSumOfHeights(self, maxHeight: List[int]) -> int:
        n = len(maxHeight)
        f = [-1] * n # f[i] 表示 i 作为峰顶左侧的高度和
        g = [-1] * n # g[i] 表示 -i-1 作为峰顶右侧的高度和
        def gao(f):
            st = []
            for i in range(len(maxHeight)):
                while st and maxHeight[i] <= maxHeight[st[-1]]:
                    st.pop()
                if st:
                    f[i] = (i - st[-1]) * maxHeight[i] + f[st[-1]]
                else:
                    f[i] = maxHeight[i] * (i + 1)
                st.append(i)
        gao(f)
        maxHeight = maxHeight[::-1]
        gao(g)
        maxHeight = maxHeight[::-1]
        ans = 0
        for i in range(len(maxHeight)):
            ans = max(ans, f[i] + g[-i-1] - maxHeight[i])
        return ans

```

**复杂度分析**

令 n 为数组长度。

* 时间复杂度：$O(n)$
* 空间复杂度：$O(n)$

> 此题解由 [力扣刷题插件](https://leetcode-pp.github.io/leetcode-cheat/?tab=solution-template) 自动生成。

力扣的小伙伴可以[关注我](https://leetcode-cn.com/u/fe-lucifer/)，这样就会第一时间收到我的动态啦\~

以上就是本文的全部内容了。大家对此有何看法，欢迎给我留言，我有时间都会一一查看回答。更多算法套路可以访问我的 LeetCode 题解仓库：https://github.com/azl397985856/leetcode 。 目前已经 40K star 啦。大家也可以关注我的公众号《力扣加加》带你啃下算法这块硬骨头。

关注公众号力扣加加，努力用清晰直白的语言还原解题思路，并且有大量图解，手把手教你识别套路，高效刷题。

![](https://p.ipic.vip/h9nm77.jpg)
