单调栈stack:
特点1:top指向最后一个数所在位置
特点2:后进先出
特点3:递增或递减 ****
例题:POJ2796
大意:求一个区间,这个区间内 所有数的和sum*区间内min 的值 相比其他区间大
输入:
6
3 1 6 4 5 2
输出:
60
3 5
问题:
- 求出每个数在哪个区间内最小: 区间怎么求 ?
- 将这个数 * 区间总值:总值怎么算?
思路:
- 标记每个数的左右边界是本身 3 [1,1] 1 [ 2, 2] 6 [ 3, 3 ] … 之后,依次入栈。
入栈前将栈顶元素与之比较 若栈顶元素较大
{ 更改栈顶右区间=入栈元素位置-1)
更改即将入栈元素左区间=栈顶元素的左区间
栈顶元素出栈
即将入栈元素入栈 }
目的:在其入栈时更改左区间,保证栈内递增,栈内元素的左区间确定;在其出栈时更改右区间,保证了左右区间最大化。 - 用 pre[ ]数组储存各个 1-i 的区间值和
之后令 a[ i ]*(pre[右]- pre[左])
代码:
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e7+10;
ll n,pre[N];
ll a[N],l[N],r[N];
ll s[N],top=0;
int t=1;
int main()
{
//scanf("%d",&t);
while(t--)
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]); //a[i]数组储存各个数
pre[i] = pre[i-1]+a[i]; //储存【1-i]的区间和值
l[i] = r[i] = i; //初始化:a[i]数组中各个数的左右边界为所在位置
}
a[n+1] = -1;
for(int i=1;i<=n+1;i++)
{
while(top && a[s[top]] > a[i]) //即将进入的数<栈顶值
{
r[s[top]] = i-1; //栈顶值的右区间更改为 i-1
l[i] = l[s[top]]; //即将进入的数 的左区间 继承 栈顶值的
top--; //将栈顶值出栈
}
s[++top] = i; //入栈
}
ll ans=-1,ansl,ansr;
for(int i=1;i<=n;i++)
{
ll temp = a[i]* (pre[r[i]]-pre[l[i]-1]) ; //让所有值依次 * 左右区间相减得出的sum
if(ans<temp) //比较出最大
{
ans = temp;
ansl = l[i];
ansr = r[i];
}
}
printf("%lld\n%lld %lld",ans,ansl,ansr);
}
return 0;
}
双向单调队列deque :
特点1: head 指向最前一个数的前一个位置
特点2: tail 指向最后一个数所在的位置
特点3: 可递增或递减
例题:POJ-2823
大意:求滚动屏(显示3个数)区间的最大值,最小值
输入:
8 3
1 3 -1 -3 5 3 6 7
输出:
-1 -3 -3 -3 3 3
3 3 5 5 6 7
Window position | Minimum | Maximum |
---|---|---|
[1 3 -1] -3 5 3 6 7 | -3 | 3 |
1 [3 -1 -3] 5 3 6 7 | -3 | 3 |
1 3 [-1 -3 5] 3 6 7 | -3 | 5 |
1 3 -1 [-3 5 3] 6 7 | -3 | 5 |
1 3 -1 [-3 5 3] 6 7 | 3 | 6 |
1 3 -1 -3 5 [3 6 7] | 3 | 7 |
代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e6+20;
const int M=10;
const int inf=0x3f3f3f3f; //无穷大
int n,k;
int a[N];
int ansmin[N],ansmax[N];
int que[N],head,tail,t=1;
int main()
{
while(t--)
{
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]); //a[i]储存数
for(int i=1;i<=n;i++)
{ //求min序列
while(tail>head && que[head+1]+k<=i) head++; //保证头+k<=尾
while(tail>head && a[que[tail]]>a[i]) tail--; //保证队列单调递增,头就是所求
que[++tail] = i; //que[]中存储的是位置
if(i>=k) ansmin[i-k+1]=a[que[head+1]]; //k及k之后的位置的有答案
}
head = tail =0;
for(int i=1;i<=n;i++) //求max序列,同上
{
while(tail>head && que[head+1]+k<=i) head++; //保证头+k<=尾
while(tail>head && a[que[tail]]<a[i]) tail--; //保证队列单调递减,头就是所求
que[++tail] = i; //que[]中存储的是位置
if(i>=k) ansmax[i-k+1]=a[que[head+1]]; //k及k之后的位置的有答案
}
for(int i=1;i<=n-k+1;i++) printf("%d%c",ansmin[i],i == n-k+1 ? '\n':' ') ;
for(int i=1;i<=n-k+1;i++) printf("%d%c",ansmax[i],i == n-k+1 ? '\n':' ') ;
}
return 0;
}
综上两题:单调栈与单调列
相同点:都将数依次入栈或入列,其间要保证其递增或递减,若不满足的数,则令其出栈或出队,并且更新部分所求,最终得出所求
不同点:
-
单调栈使用top实现后进先出
-
单调列有head可实现先进先出,tail可实现后进先出,两头控制