单调栈:直方图中最大的矩形
www.acwing.com/file_system/file/content/whole/index/content/3682…
-
对于第 i i i 个位置,在
lmax[i]
中,存储了从当前这个点向左延伸,能够延伸的最大长度,此处的延伸是指不受阻挡地向外扩展(即碰到比自己低的就停止) -
同理对于
rmax[i]
,存储了当前这个点向右侧延伸的最大长度 -
注意以上两个数组所表示的数值不包含该点自身
-
这样子对于第 i i i 个点,其所能扩展得到的最大矩形面积为 a r r [ i ] ∗ ( l m a x [ i ] + r m a x [ i ] + 1 ) arr[i]*(lmax[i]+rmax[i]+1) arr[i]∗(lmax[i]+rmax[i]+1)
-
可以思考一下为什么一定要取到 a r r [ i ] arr[i] arr[i],而不是比它更小一点的数值向外延伸
- 假设取了比 a r r [ i ] arr[i] arr[i] 更小的一个数,记为 x x x,以 $x $ 为基准向两侧扩展得到的数组为 l m a x ′ [ i ] lmax'[i] lmax′[i] 和 r m a x ′ [ i ] rmax'[i] rmax′[i]
- 如果要使 x ∗ ( l m a x ′ [ i ] + r m a x ′ [ i ] + 1 ) > a r r [ i ] ∗ ( l m a x [ i ] + r m a x [ i ] + 1 ) x*(lmax'[i]+rmax'[i]+1) > arr[i]*(lmax[i]+rmax[i]+1) x∗(lmax′[i]+rmax′[i]+1)>arr[i]∗(lmax[i]+rmax[i]+1)
- 由于 x < a r r [ i ] x<arr[i] x<arr[i],那么一定要有 l m a x ′ [ i ] + r m a x ′ [ i ] > l m a x [ i ] + r m a x [ i ] ① lmax'[i]+rmax'[i]>lmax[i]+rmax[i] ① lmax′[i]+rmax′[i]>lmax[i]+rmax[i]①(必要条件)
- 如果 ① 式成立,意味着一定存在一个 j ( j ≠ i ) j(j ≠i) j(j=i),使得 l m a x ′ [ i ] + r m a x ′ [ i ] = l m a x [ j ] + r m a x [ j ] lmax'[i]+rmax'[i]=lmax[j]+rmax[j] lmax′[i]+rmax′[i]=lmax[j]+rmax[j]
- 那么最大值选取 $j $ 即可,即不用考虑小于 a r r [ i ] arr[i] arr[i] 的值,最后一定能找到最大结果
import java.util.*;
import java.io.*;
public class Main {
static final int N = 100010;
static long[] arr = new long[N];
static int[] lmax = new int[N];
static int[] rmax = new int[N];
static int n;
static int[] q = new int[N];
static int top;
public static void main(String[] args) throws Exception {
while ((n = Reader.nextInt()) != 0) {
for (int i = 0; i < n; i++) {
arr[i] = Reader.nextLong();
}
// 向左能够延伸的长度,不包含本身
// 使用单调栈,存放索引,每次寻找第一个高度比自己小的位置
top = -1;
for (int i = 0; i < n; i++) {
while (top >= 0 && arr[q[top]] >= arr[i]) {
top--;
}
lmax[i] = top == -1 ? i : i - q[top] - 1;
q[++top] = i;
}
// 向右能够延伸的长度,不包含本身
top = -1;
for (int i = n - 1; i >= 0; i--) {
while (top >= 0 && arr[q[top]] >= arr[i]) {
top--;
}
rmax[i] = top == -1 ? n - 1 - i : q[top] - i - 1;
q[++top] = i;
}
long res = 0;
for (int i = 0; i < n; i++) {
res = Math.max(res, arr[i] * (lmax[i] + rmax[i] + 1));
}
System.out.println(res);
}
}
}