题意
给定 n 个非负整数,每个整数代表一个矩形的高度,且每个矩形的宽度均为 1。可以将这些矩形想象成一个柱形图,我们的目标是计算在这个柱形图中能够画出的最大矩形面积。对于 k 个相邻的矩形,其最大矩形面积定义为:
R(k)=k×min(h[i],h[i+1],...,h[i+k−1])R(k)=k×min(h[i],h[i+1],...,h[i+k−1])
这里,h[i] 表示第 i 个矩形的高度。
思路分析
最初的想法是通过暴力扫描来解决这个问题,即使用两层循环。对于每个矩形,我们假设它是最低的,然后向两边寻找最近的比它低的矩形。这种方法的时间复杂度为 ( O(n^2) ),对于较大的 n 来说效率较低。
为了优化这个过程,我们可以使用单调栈来有效地找到每个矩形的左右边界,即最近的比当前高度低的矩形的位置。这种方法可以将时间复杂度降低至 ( O(n) )。
单调栈的实现
-
左边界的寻找:
- 从左到右遍历每个矩形,使用栈来存储矩形的索引。对于每个矩形
h[i],我们不断弹出栈顶元素,直到栈顶元素对应的高度小于h[i]。此时,当前矩形的左边界即为栈顶元素的索引。
- 从左到右遍历每个矩形,使用栈来存储矩形的索引。对于每个矩形
-
右边界的寻找:
- 从右到左重复上述过程。这样,对于每个矩形
h[i],我们可以确定其右边界。
- 从右到左重复上述过程。这样,对于每个矩形
-
计算最大矩形面积:
- 在找到所有矩形的左右边界后,可以通过公式计算每个矩形为底边的最大矩形面积:
这里,
left[i]和right[i]分别是矩形h[i]的左边界和右边界的索引。
复杂度分析
通过使用单调栈,我们可以在单次遍历中解决问题,因此总时间复杂度为 ( O(n) )。空间复杂度为 ( O(n) ) 用于存储栈和边界数组。
示例
考虑以下输入:
h = [2, 1, 5, 6, 2, 3]
- 对于矩形
h[2] = 5,其左边界为1,右边界为5,计算得到的面积为 ( (5 - 1 - 1) \times 5 = 15 )。 - 重复此过程,最终得到最大矩形面积为
10,对应的矩形为h[3] = 6和其相邻的h[4] = 2。
通过以上方法,我们能够高效地找到给定柱形图中能够画出的最大矩形面积。
总结一下,这道题总体上基于单调栈实现,并在单调栈的遍历过程中考虑了面积叠加更新答案。
代码实现
#include <algorithm>
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
int solution(int n, std::vector<int> A) {
// Edit your code here
stack<int> st;
vector<int> vc(n, 0);
for (int i = 0; i < n; i++) {
while (!st.empty() && A[i] <= A[st.top()]) {
int tmp = st.top();
vc[tmp] += A[st.top()] * (i - tmp);
st.pop();
}
if (st.empty()) {
vc[i] += A[i] * i;
} else {
vc[i] += A[i] * (i - st.top() - 1);
}
st.push(i);
}
while (!st.empty()) {
int tmp = st.top();
vc[tmp] += A[tmp] * (n - tmp);
st.pop();
}
return *max_element(vc.cbegin(), vc.cend());
}
int main() {
// Add your test cases here
std::vector<int> A_case1 = std::vector<int>{1, 2, 3, 4, 5};
std::cout << (solution(5, A_case1) == 9) << std::endl;
return 0;
}