目录
定义
严格单调递增或单调递减的栈。
单调栈里面存放的是下标,而不是具体的值。
例题
1.HDU 1506
题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=1506
题意
求最大子矩形的面积。
题解
用单调栈来做,维护x轴上每个点对应的矩形的左右的第一个比它矮的矩形。
单调栈里的元素从栈底向栈顶递增。每个栈里的元素,它左边第一个比它小的矩形就是栈里面它底下一个元素,它右边第一个比它小的元素会在其出栈的时候计算到。
代码
#include <bits/stdc++.h>
using namespace std;
#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8
typedef long long ll;
const ll inf = 0x3f3f3f3f;
const ll maxn = 1000005;
inline void read(ll &x)
{
ll f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
inline void print(ll x)
{
if(x<0){ putchar('-'); x=-x;}
if(x>9) print(x/10);
putchar(x%10+'0');
}
ll n,a[maxn];
stack<ll> s;
int main()
{
while(cin>>n) {
if(n==0) break;
while(!s.empty())
s.pop();
for(ll i=1;i<=n;i++)
read(a[i]);
ll ans = 0;
for(ll i=1;i<=n;i++) {
ll l,r,j;
while(!s.empty()&&a[s.top()]>a[i]) { //从栈底向栈顶递增,所以a[i]必须>栈顶元素
j = s.top(); //这时候计算的是j元素的左右第一个比它小的元素
s.pop();
if(!s.empty())
l = s.top();
else
l = 0;//说明对于j来说,它前面没有比它小的元素
r = i;//由于a[l]和a[r]已经比a[j]小了,所以实际宽度应为[l+1,r-1]
ll cnt = (r-l-1)*a[j];// 计算的是弹出的元素的左右边界 ,计算的是[l+1,r-1]的距离
ans = max(ans,cnt);
}
s.push(i);
}
//第一遍处理完了,底下处理栈里的剩余元素
while(!s.empty()) {
ll l,r,j;// r就是n,因为栈里剩下的元素的右边肯定没有比它小的了
j = s.top();
s.pop();
if(!s.empty())
l = s.top();
else
l = 0;
ll cnt = (n-l)*a[j];
ans = max(ans,cnt);
}
cout<<ans<<endl;
}
return 0;
}
2.poj 2796
题目链接
http://poj.org/problem?id=2796
题意
记价值s = (a[l]+...+a[r])*min(a[l],...a[r]) ,求s的最大期望。
题解
对于任意一个a[i],如果它是最小值,那么以它为中心的最大期望是可以计算出来的,左右端点分别为第一个比它小的数。
题目就转化成了之前一道题,维护数组中每个数左右第一个比其小的数。
由于是用和去乘最小值,所以要维护一下前缀和。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;
#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8
typedef long long ll;
const ll inf = 0x3f3f3f3f;
const ll maxn = 200005;
inline void read(ll &x)
{
ll f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
inline void print(ll x)
{
if(x<0){ putchar('-'); x=-x;}
if(x>9) print(x/10);
putchar(x%10+'0');
}
ll n;
ll a[maxn],sum[maxn];
int main()
{
cin>>n;
sum[0] = 0;
for(ll i=1;i<=n;i++)
{
read(a[i]);
sum[i] = sum[i-1]+a[i];
}
stack<ll> s;
ll L,R,ans=-1;
for(ll i=1;i<=n;i++) {
while(!s.empty()&&a[s.top()]>=a[i]) {
ll j,l,r;
j = s.top();
s.pop();
if(!s.empty()) {
l = s.top();
}else {
l = 0;
}
r = i;
ll cur_ans = (sum[r-1]-sum[l-1+1])*a[j];
if(ans<cur_ans) {
ans = cur_ans;
L = l+1;
R = r-1;
}
}
s.push(i);
}
while(!s.empty()) {
ll j,l,r;
j = s.top();
s.pop();
if(!s.empty()) {
l = s.top();
}else {
l = 0;
}
r = n+1;
ll cur_ans = (sum[r-1]-sum[l-1+1])*a[j];
if(ans<cur_ans) {
ans = cur_ans;
L = l+1;
R = r-1;
}
}
cout<<ans<<endl<<L<<" "<<R<<endl;
return 0;
}
3.poj3250
题目链接
http://poj.org/problem?id=3250
题意
有n只奶牛,它们身高不一样。
奶牛只能向右看,并且只能看到比它矮的奶牛。
问所有奶牛加起来能看到多少个奶牛。
题解
由于只能向右看,所以只需要维护每个点右边第一个比它小的数的位置就行了。
由于是严格小于,所以压栈的时候要注意。
以及只有一头奶牛,并且它的高度为0的特殊情况。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8
typedef unsigned long long ull;
const int inf = 0x3f3f3f3f;
const int maxn = 200005;
inline void read(int &x)
{
int f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
inline void print(int x)
{
if(x<0){ putchar('-'); x=-x;}
if(x>9) print(x/10);
putchar(x%10+'0');
}
ull n,a[maxn],f[maxn];
stack<ull> s;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++) {
while(!s.empty()&&a[s.top()]<=a[i]) {
int j = s.top();
s.pop();
f[j] = i-j-1;
}
s.push(i);
}
while(!s.empty()) {
int j = s.top();
s.pop();
f[j] = n-j;
}
ull ans = 0;
for(int i=1;i<=n;i++)
ans += f[i];
cout<<ans<<endl;
return 0;
}
4.求01矩阵中最大子矩阵面积
题目
假如矩阵为 1 1 0 1 1 的话,最大子矩阵面积为4。
1 1 1 0 0
题解
我们用一个数组pre来表示每一行,比如第一行pre={1,1,0,1,1},接下来一行如果是1,就在pre对应下标+1,否则直接变成0,比如第二行pre={2,2,1,0,0}。pre维护的可以看作以该行为底,形成了类似第一题的n个矩形,求这些矩形的最大子矩形面积,然后把所有行都求一遍就行了。