单调栈
stack<int> s;//保存的是元素的下标
for(int i=1; i<=n; i++)
{
while(!s.empty() && a[s.top()]>=a[i])
s.pop();
if(s.empty())
L[i] = 0;
else
L[i] = s.top();//L 记录每个数左边第一个小于它的元素的位置,保存的是下标
s.push(i);
}
========================================
单调队列
poj2823 滑动窗口 单调队列
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
const int maxn = 1000000+10;
int n,k;
int maxq[maxn], minq[maxn], num[maxn];//maxq minq 保存的都是下标
int maxans[maxn], minans[maxn];
int main()
{
while(cin>>n>>k)
{
int maxhead=0, maxtail=0;
int minhead=0, mintail=0;
for(int i=0; i<n; i++)
{
// for(int w=0;w<n;w++)
// cout<<maxq[i]<<" ";
// cout<<endl;
// for(int w=0;w<n;w++)
// cout<<minq[i]<<" ";
// cout<<endl;
/*删除下标超出范围的队首元素*/
//如果队首元素(最大值)的下标已经超出了 当前位置-窗口大小k,移动
//。。。。。。最小值同最大值。。。。。。
if(maxhead<maxtail && maxq[maxhead]<=i-k) maxhead++;
if(minhead<mintail && minq[minhead]<=i-k) minhead++;
/*删除队尾元素*/
scanf("%d",&num[i]);
// cout<<maxtail<<endl;
while(maxhead<maxtail && num[maxq[maxtail-1]]<=num[i])
maxtail--;
maxtail++;
maxq[maxtail-1]=i;//maxtail 指向队尾的下一个元素 结合ppt的例子模拟即可明白
while(minhead<mintail && num[minq[mintail-1]]>=num[i])
mintail--;
mintail++;
minq[mintail-1]=i;
// cout<<maxhead<<" "<<maxq[maxhead]<<endl;
// cout<<minhead<<" "<<minq[minhead]<<endl;
maxans[i]=num[maxq[maxhead]];
minans[i]=num[minq[minhead]];
}
for(int i=k-1; i<n; i++)
cout<<minans[i]<<" ";
cout<<endl;
for(int i=k-1; i<n; i++)
cout<<maxans[i]<<" ";
cout<<endl;
}
return 0;
}
/*
设置两个单调队列分别记录最大值和最小值。
对于每一个新读入的数字,进行两次操作(对于求最大值和最小值中的某一个而言),
一是若队首不在滑窗范围内则删去;
二是删去队末比当前值小(或大)的值,并将当前值插入对尾。
每一次的最小(大)值就是当前单调队列的队首。
*/
============================================
尺取法
poj3061
============================================
练习题
hdu1506
- 单调栈
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<cstdlib>
using namespace std;
typedef long long ll;
const int maxn=1e3+10;
#define N 100005
int q[N]={-1},w[N];//w记录左宽;
int main()
{
int n,h;
while(scanf("%d",&n)&&n)
{
int top=0;
ll ans=0;
for(int i=1;i<=n+1;i++)
{
if(i!=n+1)
scanf("%d",&h);
else
h=0;
if(h>q[top])
q[++top]=h,w[top]=1;
else
{
ll cnt=0; //第一个出栈的右宽为0;
while(h<=q[top])
{
ans=max(ans,(w[top]+cnt)*q[top]); //(左宽+右宽)*高度;
cnt=cnt+w[top--]; //第 (i>1)出栈的右宽为上一个的总宽;
}
// 终于找到比自己小的数字了,可以入栈了,入栈会得到左宽,左宽为上一个出栈元素的总宽+1;
q[++top]=h;
w[top]=cnt+1;
}
}
printf("%I64d\n",ans);
}
return 0;
}
- 递归
#include<iostream>
#include<algorithm>
#include<stack>
using namespace std;
int main() {
while(true) {
int n;
cin>>n;
if(n==0) break;
long long int *a=new long long int[n+1];
int *l=new int[n+1];
int *r=new int[n+1];
for(int i=0; i<n; i++) {
l[i]=r[i]=i;
cin>>a[i];
}
for(int i=1; i<n; i++)
while(l[i]>=1&&a[l[i]-1]>=a[i])
l[i]=l[l[i]-1];
for(int i=n-2; i>=0; i--)
while(r[i]<=n-2&&a[r[i]+1]>=a[i])
r[i]=r[r[i]+1];
long long int res=0;
for(int i=0; i<n; i++) {
if(res<a[i]*(r[i]-l[i]+1))
res=a[i]*(r[i]-l[i]+1);
}
cout<<res<<endl;
}
return 0;
}
hdu1506
hdu1506
- 单调栈