题解16. 最大矩形面积问题 | 豆包MarsCode AI刷题

90 阅读3分钟

题意

给定 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) )。

单调栈的实现

  1. 左边界的寻找

    • 从左到右遍历每个矩形,使用栈来存储矩形的索引。对于每个矩形 h[i],我们不断弹出栈顶元素,直到栈顶元素对应的高度小于 h[i]。此时,当前矩形的左边界即为栈顶元素的索引。
  2. 右边界的寻找

    • 从右到左重复上述过程。这样,对于每个矩形 h[i],我们可以确定其右边界。
  3. 计算最大矩形面积

    • 在找到所有矩形的左右边界后,可以通过公式计算每个矩形为底边的最大矩形面积:
    Area(i)=(right[i]left[i]1)×h[i]\text{Area}(i) = (right[i] - left[i] - 1) \times 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;
}