在例题中,题目要求我们找到 每个数左边第一个比它小的数
我们可以先写出暴力做法,然后进一步优化
for(int i = 0; i < n; i ++ )
for(int j = i - 1; j >= 0; j -- )
if(a[i] > a[j])
{
cout << a[j] << endl;
break;
}
我们可以用一个 栈 来维护相关的数据
我们可以发现,在栈中,有一些元素是不会作为答案输出的
假设 且满足 ,那么 x 就不会作为答案输出,因此,在栈中可以维护一个单调上升的序列(每一次判断都可以删除所有逆序的点)
AC代码
#include <iostream>
using namespace std;
const int N = 100010;
int n;
int stk[N], tt;
int main()
{
cin >> n;
for(int i = 0; i < n; i ++ )
{
int x;
cin >> x;
while(tt && stk[tt] >= x) tt -- ;
if(tt) cout << stk[tt] << ' ';
else cout << -1 << ' ';
stk[++ tt] = x;
}
return 0;
}
例题 Acwing131. 直方图中最大的矩形
输入样例:
7 2 1 4 5 1 3 3 4 1000 1000 1000 1000 0
输出样例:
8 4000
解题思路
假设所有的矩形的高度是从左向右单调递增的,那么答案是多少?
显而易见的,我们可以尝试以每个矩形的高度作为最终矩形的高度,并把宽度延伸到右边界,得到一个矩形,在所有的矩形面积中取最大值就是答案,如下图所示:
如果下一个矩形的高度比上一个小,那么该矩形想利用之前的矩形一起构成一个较大的面积的时候,这块面积的高度不可能超过该矩形自己的高度。换句话说,在考虑上述的几种情况后,下图中红色框框中的部分就丝毫没有用处了。
既然没有用处,我们可以把这些比该矩形高的矩形都删掉,用一个宽度累加、高度为该矩形自己的高度的新矩形(上图中的阴影部分)代替。这样不会对后续的计算产生影响。
于是我们维护的轮廓就变成了一个高度单调递增的矩形序列,问题变得可解。
详细的说,我们建立一个栈,用来保持若干个矩形,这些矩形的高度是单调递增的。
我们从左到右一次扫描每个矩形。
如果当前矩形比栈顶矩形高,直接进栈。
否则不断弹出栈顶,直到栈顶为空或者是栈顶的矩形高度比当前矩形小。在出栈的过程中,
我们累计计算被弹出的矩形的宽度之和,并且每弹出一个矩形,就用它的高度乘上累计的
宽度去更新答案。整个出栈过程结束后,我们把一个高度为当前矩形、宽度为累计值的新矩
形入栈。
简单来说,就是找到每一个高度,左边第一个比它矮的位置 ,对应的面积就是
AC代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
typedef long long LL;
int n;
int h[N], l[N], r[N], q[N];
int main()
{
while(cin >> n, n)
{
for(int i = 1; i <= n; i ++ ) scanf("%d", &h[i]);
int tt = 0;
h[0] = h[n + 1] = -1;
q[0] = 0;
for(int i = 1; i <= n; i ++ )
{
while(h[i] <= h[q[tt]]) tt --;
l[i] = q[tt];
q[++ tt] = i;
}
tt = 0;
q[0] = n + 1;
for(int i = n; i ; i -- )
{
while(h[i] <= h[q[tt]]) tt --;
r[i] = q[tt];
q[ ++ tt] = i;
}
LL res = 0;
for(int i = 1; i <= n; i ++ )
res = max(res, (LL)h[i] * (r[i] - l[i] - 1));
cout << res << endl;
}
return 0;
}