Medium
题目描述
给你一个下标从 0 开始、长度为 n 的整数数组 maxHeights 。
你的任务是在坐标轴上建 n 座塔。第 i 座塔的下标为 i ,高度为 heights[i] 。
如果以下条件满足,我们称这些塔的配置是 美丽的 :
1 <= heights[i] <= maxHeights[i]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],总和为 13 。这个配置美丽是因为:
- 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],总和为 22 。这个配置美丽是因为:
- 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],总和为 18 。这个配置美丽是因为:
- 1 <= heights[i] <= maxHeights[i]
- heights 是山脉数组,峰值在 i = 2 处。
注意,对于这个配置,i = 3 也可以是一个峰值。
可以证明不存在和大于 18 的美丽配置。
提示:
1 <= n == maxHeights.length <= 10^51 <= maxHeights[i] <= 10^9
解题思路
这道题要求我们找到一个山脉数组的配置,使得塔的高度总和最大。关键思路是枚举每个位置作为山脉的峰值。
解题思路
对于每个可能的峰值位置 i,我们需要计算:
- 左半部分:从位置 0 到 i 的最大高度和(非递减序列)
- 右半部分:从位置 i 到 n-1 的最大高度和(非递增序列)
核心观察
为了构造山脉数组:
- 左半部分必须是非递减的,每个位置的高度不能超过
maxHeights[i] - 右半部分必须是非递增的,每个位置的高度不能超过
maxHeights[i]
动态规划 + 单调栈优化
使用两次遍历:
- 从左到右:计算
left[i]表示以 i 为峰值时,左半部分(0到i)的最大高度和 - 从右到左:计算
right[i]表示以 i 为峰值时,右半部分(i到n-1)的最大高度和
关键优化是使用单调栈找到每个位置左边/右边第一个更小的元素,这样可以快速计算区间内的高度和。
对于位置 i:
left[i] = maxHeights[i] * (i - j) + left[j],其中 j 是 i 左边最近的且maxHeights[j] < maxHeights[i]的位置right[i] = maxHeights[i] * (j - i) + right[j],其中 j 是 i 右边最近的且maxHeights[j] < maxHeights[i]的位置
最终答案是 max(left[i] + right[i] - maxHeights[i]) 对所有 i 的最大值。
代码实现
class Solution {
public:
long long maximumSumOfHeights(vector<int>& maxHeights) {
int n = maxHeights.size();
vector<long long> left(n), right(n);
stack<int> st;
// 计算left数组
for (int i = 0; i < n; i++) {
while (!st.empty() && maxHeights[st.top()] > maxHeights[i]) {
st.pop();
}
if (st.empty()) {
left[i] = (long long)maxHeights[i] * (i + 1);
} else {
int j = st.top();
left[i] = left[j] + (long long)maxHeights[i] * (i - j);
}
st.push(i);
}
// 清空栈
while (!st.empty()) st.pop();
// 计算right数组
for (int i = n - 1; i >= 0; i--) {
while (!st.empty() && maxHeights[st.top()] > maxHeights[i]) {
st.pop();
}
if (st.empty()) {
right[i] = (long long)maxHeights[i] * (n - i);
} else {
int j = st.top();
right[i] = right[j] + (long long)maxHeights[i] * (j - i);
}
st.push(i);
}
long long result = 0;
for (int i = 0; i < n; i++) {
result = max(result, left[i] + right[i] - maxHeights[i]);
}
return result;
}
};
class Solution:
def maximumSumOfHeights(self, maxHeights: List[int]) -> int:
n = len(maxHeights)
left = [0] * n
right = [0] * n
stack = []
# 计算left数组
for i in range(n):
while stack and maxHeights[stack[-1]] > maxHeights[i]:
stack.pop()
if not stack:
left[i] = maxHeights[i] * (i + 1)
else:
j = stack[-1]
left[i] = left[j] + maxHeights[i] * (i - j)
stack.append(i)
# 清空栈
stack.clear()
# 计算right数组
for i in range(n - 1, -1, -1):
while stack and maxHeights[stack[-1]] > maxHeights[i]:
stack.pop()
if not stack:
right[i] = maxHeights[i] * (n - i)
else:
j = stack[-1]
right[i] = right[j] + maxHeights[i] * (j - i)
stack.append(i)
result = 0
for i in range(n):
result = max(result, left[i] + right[i] - maxHeights[i])
return result
public class Solution {
public long MaximumSumOfHeights(IList<int> maxHeights) {
int n = maxHeights.Count;
long[] left = new long[n];
long[] right = new long[n];
Stack<int> stack = new Stack<int>();
// 计算left数组
for (int i = 0; i < n; i++) {
while (stack.Count > 0 && maxHeights[stack.Peek()] > maxHeights[i]) {
stack.Pop();
}
if (stack.Count == 0) {
left[i] = (long)maxHeights[i] * (i + 1);
} else {
int j = stack.Peek();
left[i] = left[j] + (long)maxHeights[i] * (i - j);
}
stack.Push(i);
}
// 清空栈
stack.Clear();
// 计算right数组
for (int i = n - 1; i >= 0; i--) {
while (stack.Count > 0 && maxHeights[stack.Peek()] > maxHeights[i]) {
stack.Pop();
}
if (stack.Count == 0) {
right[i] = (long)maxHeights[i] * (n - i);
} else {
int j = stack.Peek();
right[i] = right[j] + (long)maxHeights[i] * (j - i);
}
stack.Push(i);
}
long result = 0;
for (int i = 0; i < n; i++) {
result = Math.Max(result, left[i] + right[i] - maxHeights[i]);
}
return result;
}
}
var maximumSumOfHeights = function(maxHeights) {
const n = maxHeights.length;
const leftSum = new Array(n).fill(0);
const rightSum = new Array(n).fill(0);
const leftStack = [];
const rightStack = [];
// Calculate left sums using monotonic stack
for (let i = 0; i < n; i++) {
while (leftStack.length > 0 && maxHeights[leftStack[leftStack.length - 1]] >= maxHeights[i]) {
leftStack.pop();
}
if (leftStack.length === 0) {
leftSum[i] = (i + 1) * maxHeights[i];
} else {
const prevIndex = leftStack[leftStack.length - 1];
leftSum[i] = leftSum[prevIndex] + (i - prevIndex) * maxHeights[i];
}
leftStack.push(i);
}
// Calculate right sums using monotonic stack
for (let i = n - 1; i >= 0; i--) {
while (rightStack.length > 0 && maxHeights[rightStack[rightStack.length - 1]] >= maxHeights[i]) {
rightStack.pop();
}
if (rightStack.length === 0) {
rightSum[i] = (n - i) * maxHeights[i];
} else {
const nextIndex = rightStack[rightStack.length - 1];
rightSum[i] = rightSum[nextIndex] + (nextIndex - i) * maxHeights[i];
}
rightStack.push(i);
}
let maxSum = 0;
for (let i = 0; i < n; i++) {
const totalSum = leftSum[i] + rightSum[i] - maxHeights[i];
maxSum = Math.max(maxSum, totalSum);
}
return maxSum;
};
复杂度分析
| 复杂度类型 | 值 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 两次遍历数组,单调栈保证每个元素最多入栈出栈一次 |
| 空间复杂度 | O(n) | 需要 left、right 数组和单调栈空间 |