我是怎么引入这个单调栈的呢,我是先想的是求一个直方图中的最大矩形,然后是简化这个模型,我假设这个直方图是有序的,那我会怎么求呢。我是会选择从高的矩形往低的矩形求,每次求出当前高度最低的矩形的面积和上一个矩形的面积的大小取较大的即可,那可以看出,这个的要求是在有序的情况下,从高往低更新矩形大小,那么可以看出,矩形的大小最终是由一个区间矩形最低高度和最长宽度乘积(区间长度为1-n)
那么解决方法是什么呢:
首先,从高的往低的选择,每次往前选择矩形,都要更新所求矩形的最大值(在上一次的面积,和从n-i最低矩形(因为有序,所以就是当前矩形的高度,如果是第一次就直接算自己就行)高度(宽度)*(n-i+1也就是长度))。
好,简化版解决了,那我怎么解决无序的直方图呢,我是想尽量把他变成有序的直方图然后来按照简化版的来解决的,那就是呢,一定有序区间选取比较低的矩形高度来计算最大的面积,那我假设从前往后的时候,第i个矩形都是按照高度排列的,当我放入第i+1个矩形的时候,我直接求第i个矩形时的面积和加入当前矩形之后的面积的值得最大值,那就会分两种情况,
1.i+1得高于之前的所有矩形,那么我就直接放入”容器“这个矩形时第多少个(也就是下标,用来计算宽度)。 等到我有一个比这个最高矩形矮的时候来求(因为我不知道第i+1个矩形得高度是不是最高的,我可能第i+2个时更高的,那又得计算一遍最大值。事实上,每当我加入一个矩形的时候,我的当前矩形长会增加1 ,宽会也可能会改变。),
假设白色得是宽,红色得是长 ,当我加入一个更高得矩形的时候,我的长和宽都可能改变,但是当我加入一个比较矮的矩形的时候,我的宽是一定不会改变的,这样就比较好计算,所以我选择到当加入矩形比较低的时候我计算
2.那我这个i+1矩形如果是比前面小得话,那我就开始计算前i+1个矩形所能形成的矩形的最大值。
好,那么问题就转化成了我前面问题得简化版,有一个有序的直方图,我想求这个最大的矩形面积。
那我要解决得问题就是求这个面积,我要解决的是呢,就是用什么容器来存这个矩形的下标,因为我是选择从最高的矩形往矮的矩形开始求得,所以我就需要一个容器来存,这个容器的特点呢,也就是后进先出了,因为当我出现一个矮的矩形的时候,我用最高的矩形和前面的矩形来算最大矩形,那理所当然就是算最高的和次高的,然后再算次次高的矩形,那么就会用到栈,但是因为栈是没有单调性的,所以就会用到单调栈,计算最大面积的时候呢,就用下标来算长,然后宽就是比较矮的那个矩形的高。
那我这个计算完之后,我再把这个上一个区间中的(l,r)中最后一个计算的下标得到,把这个r+1的高赋值给这个第l个矩形,然后继续往后遍历,循环往复即可,为什么要这样做呢,是因为每增加一个矩形,我的矩形的长的数量就会增加,我得记录当前最大的高,和当前最大的长即可。
Code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define std std::ios::sync_with_stdio(0)
#define mem(a,b) memset(a,b,sizeof(a));
#define P pair<int,int>
#define nn '\n'
const int INF = 0x3f3f3f3f;
const int inf = 2147483647;
const int maxn = 1e5+10;
const int maxm = 3e5 + 10;
stack<int>sta;
int a[maxn];
ll max(ll a, ll b)
{
return a > b ? a : b;
}
int max(int a, int b)
{
return a > b ? a : b;
}
int main()
{
int n;
std;
while ((cin >> n) && n != 0)
{
for (int i = 1; i <= n; i++)
cin >> a[i];
a[n + 1] = -1; //
ll ans = 0;
for (int i = 1; i <= n + 1; i++)
{
if (sta.empty() || a[sta.top()] <= a[i]) //因为如果只有一个数,那一定是有序的,
sta.push(i); //又因为我要的是一个非降序的高的栈,所以就放入
else
{
int sign;
while (!sta.empty()&&a[sta.top()] > a[i]) //如果栈不是空的,并且栈顶元素大于当前元素就说明找到了减小的高
{ //就计算面积的最大值
sign=sta.top();
//ll ans1 = a[sign] * (i -sign + 1);因为我这个时候的i是这个比较矮的矩形的i,就是最大矩形下标加1.
ll ans1 = a[sign] * (i - sign); //我既然是使用最大矩形来算,就不用再加一
ans = max(ans, ans1);
sta.pop();
}
a[sign] = a[i]; //更新最大高度
sta.push(sign); //并且保留之前矩形的宽度
}
}
cout << ans << nn;
}
return 0;
}