acwing 131.直方图中最大的矩阵
算法:(数组模拟栈+单调栈)
思路:
1.首先先思考最大矩形的特点,一定是以某个矩形i的高为最大矩形的高,然后以这个矩形i往两边扩展而来的最大矩形;所以想到暴力做法就是遍历i,每个矩形i往两边扩展出一个以矩形i的高为高的最大矩形,所以,然后更新答案,遍历完成之后就得到最终答案。
2.暴力的思路看看n的范围10^5如果双重循环就TLE了,所以想着怎么去优化它;比如对矩形i往左边扩展是,如果扩展到左边的一个比较矮矩形,那么,更左边更高的矩形就被限制住了,不会拿来构成最大矩形,所以就不会存在递减序列。联想到单调栈,假设我们维护了一个单调递增栈,矩形i就能寻找到距离它最近的比它矮的矩形下标j,那么j到i之间的都是和矩形i等高或者更高的矩形,自然就能构成最大矩形;右边同理
3.向左和向右都要维护一个单调栈,然后记录距离它最近的比它矮的矩形下标,就要l和r的两个数组来存
4.每个矩形高大于等于0,矩形标号从1~n,所以在边界的时候很容易越界过去,比如矩形i往左寻第一个比它矮的矩形时,可能寻找下标1,但是此时万一下标1的矩形高等于0,那么栈弹出的时候会因为0号位好似有个矩形高为0,把它弹出,就会继续向左好像寻找到数组里0标号的高度默认为0的矩形,但其实没有这个矩形,那么就会寻找错误;因为这种情况的存在,所以我们直接在左边界更左边0号位放置一个-1高的矩形,在右边界更右边n+1号位放置-1高的矩形,这样就不会寻找左右边界的时候超出去了
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+5;
typedef long long ll;
int h[N],l[N],r[N]; //l记录往左寻的最靠近矩形i的比它矮的矩形标号,r记录往右寻的靠近矩形i的比它矮的矩形标号
int stk[N]; //拿来维护单调栈的模拟数组,记录下标
int main ()
{
int n;
while (cin>>n,n)
{
for (int i=1;i<=n;i++) cin>>h[i];
h[0]=h[n+1]=-1; //防止寻找下标越界
stk[0]=0; //因为设置了一个-1高的0号矩形,所以提前入栈0标号
int tt=0; //栈0号位已经有东西了,所以tt从0开始
for (int i=1;i<=n;i++)
{
while (h[stk[tt]]>=h[i]) tt--; //已经入栈了初始标号,所以不用判断栈非空;遇到非递增,就弹出栈顶
l[i]=i-stk[tt]-1; //l数组记录矩形i到栈顶记录的标号之间有多少个比矩形i更高或者等高的矩形
stk[++tt]=i;
}
stk[0]=n+1; //因为设置了一个-1高的n+1号矩形,所以提前入栈n+1标号
tt=0; //同上
for (int i=n;i>=1;i--) //注意这里是从n号位开始到1号位了
{
while (h[stk[tt]]>=h[i]) tt--;
r[i]=stk[tt]-i-1; //同上
stk[++tt]=i;
}
ll ans=0; //宽是10^5数量级,高是10^9数量级,10^5*10^9会爆int,所以用long long,下面的潜质类型转换也是为了防止爆int
for (int i=1;i<=n;i++) ans=max(ans,(ll)(l[i]+r[i]+1)*h[i]); //答案更新;然后宽的时候要加上1,因为还有矩形i本身也算一个
cout<<ans<<endl;
}
return 0;
}