最大矩形面积问题

153 阅读4分钟

题目分析与解法思路

一、题目分析

给定一个数组 h,要求我们对于任意长度为 k (1 ≤ k ≤ N)的一组相邻元素,计算该组元素所能形成的最大矩形面积。对于长度为 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] 表示数组中的元素,min(h[i], h[i+1], ..., h[i+k-1]) 表示这 k 个相邻元素中的最小值。我们的目标是计算出对于每个 k,最大化 R(k),并返回其最大值。

二、问题分析
  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])

    这个公式表达的是,在这 k 个相邻元素中,我们以最小值作为矩形的高度,k 作为矩形的宽度,计算该矩形的面积。

  2. 暴力解法: 对于每个 k(从 1 到 N),我们可以遍历数组中所有长度为 k 的子数组,找到每个子数组的最小值并计算矩形面积。这样,我们可以得到一个包含所有 k 的最大矩形面积的集合。

    但暴力解法的时间复杂度较高,尤其是在 N 较大的情况下,每次计算一个子数组的最小值需要 O(k) 时间,所以总体时间复杂度是 O(N^2),这对于大数据规模来说会显得非常慢。

三、单调队列与滑动窗口优化

滑动窗口 是一种常用的技巧,特别适合用于处理需要连续子数组(或子区间)问题的场景。在本题中,我们需要对于每个 k 计算连续的 k 个元素中的最小值,而滑动窗口技术可以帮助我们高效地实现这一点。

单调队列 是一种特殊的队列,它可以在 O(1) 的时间内得到当前窗口中的最小值或最大值。对于本题,我们可以使用单调队列来维护当前窗口中的最小值,队列中的元素按从小到大的顺序排列,这样队首元素就是当前窗口的最小值。

操作步骤

  • 当我们滑动窗口时,队列会自动保持队首是当前窗口的最小值。
  • 每次窗口滑动时,我们移除队列头部不在当前窗口中的元素,并把新的元素加入队列,同时保持队列中的元素单调性。

通过使用单调队列,我们可以在 O(1) 的时间内得到当前窗口的最小值,从而使得每次窗口滑动的计算时间复杂度降到 O(1)。 from collections import deque

def solution(n, array): max_area = 0 # 记录最大矩形面积

# 枚举每个 k (1 <= k <= N)
for k in range(1, n + 1):
    deq = deque()  # 双端队列用于维护当前窗口的最小值索引
    # 初始填充前 k 个元素的窗口
    for i in range(k):
        # 维护队列,使得队列中的元素按从小到大的顺序排列
        while deq and array[deq[-1]] >= array[i]:
            deq.pop()
        deq.append(i)
    
    # 计算第一个窗口的最大矩形面积
    max_area = max(max_area, k * array[deq[0]])

    # 滑动窗口,计算每个位置的矩形面积
    for i in range(k, n):
        # 移除不在窗口范围内的元素
        while deq and deq[0] <= i - k:
            deq.popleft()
        
        # 插入新的元素,维护单调性
        while deq and array[deq[-1]] >= array[i]:
            deq.pop()
        deq.append(i)
        
        # 计算当前窗口的最大矩形面积
        max_area = max(max_area, k * array[deq[0]])

return max_area

测试用例

if name == "main": print(solution(5, [1, 2, 3, 4, 5])) # Expected output: 9

五、详细解读
  1. 初始化

    • max_area 用于记录计算过程中遇到的最大矩形面积。
    • deq 是一个双端队列,用于高效地计算每个窗口内的最小值。
  2. 滑动窗口操作

    • 对于每个 k(从 1 到 N),首先计算第一个长度为 k 的窗口的最小值,并更新 max_area

    • 然后,滑动窗口,每次滑动时:

      • 删除队列头部不在当前窗口内的元素。
      • 添加新的元素,并保持队列中的元素按照从小到大的顺序排列,这样队首的元素就是当前窗口的最小值。
  3. 面积计算

    • 每次更新窗口时,计算当前窗口的矩形面积 k * array[deq[0]],并更新 max_area
  4. 返回结果

    • 最终返回 max_area,即所有 k 长度窗口的最大矩形面积。
七、总结

这道题考察了滑动窗口单调队列的应用。通过双端队列,能够在 O(1) 的时间内计算每个窗口的最小值,从而避免了暴力解法中的高时间复杂度,使得问题能够更高效地解决。虽然最终的时间复杂度是 O(N^2),但在许多实际情况下,这种优化方式相较于暴力解法仍然能提供显著的性能提升。