//顾名思义, 就是维护一个严格单调递增或递减的序列. 队列和栈的区别就是他们所特有的性质, 用数组模拟.
poj 2823 单调队列
//这个就是一个最简单的单调队列的应用, 具体解释看代码. 后面有进阶的单调队列 .
const int maxn=1e6+5;
int maxq[maxn],minq[maxn];
int maxans[maxn],minans[maxn],num[maxn];
int head1,tail1;
int head2,tail2;
void solve()
{
int n,k;
while(scanf("%d%d",&n,&k)!=EOF){
for(int i=0;i0;
if(head1q[head1] <= i - k) head1++;
if(head2q[head2] <= i - k) head2++;
scanf("%d",&num[i]);
while( head1q[tail1-1]] <= num[i]) tail1--;
tail1++;
maxq[tail1-1] = i;
while( head2q[tail2-1]] >= num[i]) tail2--;
tail2++;
minq[tail2-1]=i;
maxans[i]=num[maxq[head1]];
minans[i]=num[minq[head2]];
}
for(int i=k-1;iprintf("%d%c",minans[i],i==n-1?'\n':' ');
for(int i=k-1;iprintf("%d%c",maxans[i],i==n-1?'\n':' ');
}
}
AC Code 记得用C++, G++会T.
const int maxn=1e6+5;
int maxq[maxn],minq[maxn]; //单调队列的都是存的那个对应的数的下标.
int maxans[maxn],minans[maxn],num[maxn];
int head1,tail1;
int head2,tail2;
void solve()
{
int n,k;
while(scanf("%d%d",&n,&k)!=EOF){
for(int i=0;i0;
//始终保持一个长度为k的队列,这里面的最大值和最小值都是在长度为k的序列中可用的.
//除非判一下是否当前窗口已经越过了它,就是判断一下下标.
//删除下标超出范围的对首元素.
if(head1q[head1] <= i - k) head1++;
if(head2q[head2] <= i - k) head2++;
//删除队尾元素
scanf("%d",&num[i]);
while( head1q[tail1-1]] <= num[i]) tail1--;
tail1++; //就是不断让对首位最大,如果后面进队的比当前的队尾大,就删去队尾.否则就添加进去.
maxq[tail1-1] = i; //这样当对首被删去后,依然可以保证后面一个就是最大.
while( head2q[tail2-1]] >= num[i]) tail2--;
tail2++;
minq[tail2-1]=i;
maxans[i]=num[maxq[head1]];
minans[i]=num[minq[head2]];
}
for(int i=k-1;iprintf("%d%c",minans[i],i==n-1?'\n':' ');
for(int i=k-1;iprintf("%d%c",maxans[i],i==n-1?'\n':' ');
}
}
HDU – 3530 单调队列进阶
//题意: 求一个最长子串其中 最大值 - 最小值 要介于m和k之间. (下次看到类似的题目就要想起这道题哈)
//思路: 用二个单调队列维护最大和最小值.
//推荐一道类似的题: 点这呀
// 提示: 串的长度和序列个数的关系!!!
AC Code
/** @Cain*/
const int maxn=1e5+5;
int a[maxn];
int maxq[maxn],minq[maxn];
int head1,tail1;
int head2,tail2;
void solve()
{
int n,m,k;
while(~scanf("%d%d%d",&n,&m,&k)){
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
head1 = tail1 = head2 = tail2 = 0;
int res = 0;
int tmp = 0; //保存上一次的位置.
for(int i=1;i<=n;i++){
while(head1 < tail1 && a[maxq[tail1-1]] < a[i] ) tail1--;
while(head2 < tail2 && a[minq[tail2-1]] > a[i] ) tail2--;
maxq[tail1++] = i;
minq[tail2++] = i;
while(a[maxq[head1]] - a[minq[head2]] > k){
if(maxq[head1] < minq[head2] ) tmp = maxq[head1++] ; //头指针移动,并且tmp记录改变位置.
else tmp = minq[head2++] ; //请一定注意好下标位置关系.
} //我的tmp从零开始,所以长度就是当前的i-tmp. 不懂就把图画出来.
if(a[maxq[head1]] - a[minq[head2]] >= m) res = max(res,i-tmp);
}
printf("%d\n",res);
}
}
poj – 2559 单调栈
//题意 : 给定从左到右多个矩形,已知这此矩形的宽度都为1,长度不完全相等。这些矩形相连排成一排,求在这些矩形包括的范围内能得到的面积最大的矩形,打印出该面积。所求矩形可以横跨多个矩形,但不能超出原有矩形所确定的范围
//思路 : 建立一个单调递增栈,所有元素各进栈和出栈一次即可。每个元素出栈的时候更新最大的矩形面积.
设栈内的元素为一个二元组(x, y),x表示矩形的高度,y表示矩形的宽度.
若原始矩形高度分别为2,1,4,5,1,3,3高度为2的元素进栈,
当前栈为(2,1)
高度为1的元素准备进栈,但必须从栈顶开始删除高度大于或等于1的矩形,因为2已经不可能延续到当前矩形。删除(2,1)这个元素之后,更新最大矩形面积为2*1=2,然后把它的宽度1累加到当前高度为1的准备进栈的矩形,然后进栈,当前栈为(1,2)
高度为4的元素进栈,当前栈为(1,2) (4,1)
高度为5的元素进栈,当前栈为(1,2) (4,1) (5,1)
高度为1的元素准备进栈,删除(5,1)这个元素,更新最大矩形面积为5*1=5,把1累加到下一个元素,得到(4,2),删除(4,2),更新最大矩形面积为4*2=8,把2累加到下一个元素,得到(1,4),1*4=4<8,不必更新,删除(1,4),把4累加到当前准备进栈的元素然后进栈,当前栈为(1,5)
高度为3的元素进栈,当前栈为(1,5) (3,1)
高度为3的元素准备进栈,删除(3,1),不必更新,把1累加到当前准备进栈的元素然后进栈,当前栈为(1,5) (3,2)
把余下的元素逐个出栈,(3,2)出栈,不必更新,把2累加到下一个元素,当前栈为(1,7),(1,7)出栈,不必更新. 栈空,结束.
最后的答案就是8.
空板:
const int maxn=1e5+5;
int a[maxn];
struct node
{
int h,w;
}s[maxn];
void solve()
{
int n;while(~scanf("%d",&n) && n){
for(int i=0;iscanf("%d",&a[i]);
}
Fill(s,0);
int top = 0;
ll ans = 0;
ll tot = 0;
for(int i=0;i0;
while(top > 0 && s[top-1].h >= a[i]){
tot = s[top-1].h * (s[top-1].w + tmp);
if(tot > ans) ans =tot;
tmp += s[top-1].w;
top--;
}
s[top].h = a[i];
s[top].w = 1+tmp;
top++;
}
ll tmp = 0;
while(top>0){
tot = s[top-1].h * (s[top-1].w + tmp);
if(tot > ans) ans =tot;
tmp += s[top-1].w;
top--;
}
printf("%lld\n",ans);
}
}
解释版:
const int maxn=1e5+5;
int a[maxn];
struct node
{
int h,w;
}s[maxn];
void solve()
{
int n;while(~scanf("%d",&n) && n){
for(int i=0;iscanf("%d",&a[i]);
}
Fill(s,0);
int top = 0;
ll ans = 0;
ll tot = 0;
for(int i=0;i0; //记录要将宽度累加的下一个的点的值.
while(top > 0 && s[top-1].h >= a[i]){
tot = s[top-1].h * (s[top-1].w + tmp); //乘之前累加的宽度.
if(tot > ans) ans =tot;
tmp += s[top-1].w; //把累加的宽度记下来.
top--; //如果可以删,就把官渡给一个,这样才能尽量使结果最大,
} //如果不删了,就把累加的结果给新入栈的元素.
s[top].h = a[i];
s[top].w = 1+tmp;
top++;
}
ll tmp = 0;
while(top>0){
tot = s[top-1].h * (s[top-1].w + tmp);
if(tot > ans) ans =tot;
tmp += s[top-1].w;
top--;
}
printf("%lld\n",ans);
}
}
CF – 817D 思维 + 单调栈
//题意: 给你个a序列, 求a序列的任意连续子序列中最大值-最小值的值的和.
//思路: 我们考虑每一个数对答案的贡献, 在一段连续的区间中, 这个数要么作为最大值, 要么作为最小值, 这样我们就设L[i]是第 i 个数它往左最大可以到的位置, R[i]是 i 往右最大可以到的位置, 这样答案就加上
ans += a[i] ×(i - L[i])×(R[i] - i ). 同理一样找最小的位置, ans-= a[i] ×(i - L[i])×(R[i] - i ), 然后输出答案即可.
AC Code (空)
/** @Cain*/
const int maxn=1e6+5;
int a[maxn],pos[maxn];
int maxl[maxn],maxr[maxn];
int minl[maxn],minr[maxn];
void solve()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
a[0] = inf;
int top = 1;
pos[top] = 0;
for(int i=1;i<=n;i++){
while(a[pos[top]] <= a[i]) top--;
maxl[i] = pos[top];
pos[++top] = i;
}
a[n+1] = inf;
top = 1;
pos[top] = n+1;
for(int i=n;i>=1;i--){
while(a[pos[top]] < a[i]) top--;
maxr[i] = pos[top];
pos[++top] = i;
}
ll res = 0;
for(int i=1;i<=n;i++) res += 1ll * a[i] * (i - maxl[i] ) * ( maxr[i] - i);
a[0] = -inf;
top = 1;
pos[top] = 0;
for(int i=1;i<=n;i++){
while(a[pos[top]] >= a[i]) top--;
minl[i] = pos[top];
pos[++top] = i;
}
a[n+1] = -inf;
top = 1;
pos[top] = n+1;
for(int i=n;i>=1;i--){
while(a[pos[top]] > a[i]) top--;
minr[i] = pos[top];
pos[++top] = i;
}
for(int i=1;i<=n;i++) res -= 1ll * a[i] * (i - minl[i] ) * ( minr[i] - i );
printf("%I64d\n",res);
}
解释版:
/** @Cain*/
const int maxn=1e6+5;
int a[maxn],pos[maxn];
int maxl[maxn],maxr[maxn];
int minl[maxn],minr[maxn];
void solve()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
a[0] = inf;
int top = 1;
pos[top] = 0;
//往左找最大到的位置.
for(int i=1;i<=n;i++){
while(a[pos[top]] <= a[i]) top--;
maxl[i] = pos[top]; //好好理解, 如果当前位大于了上一位, 则top可以直接跳到大于上一位的位置
pos[++top] = i; //在进行判断, 这样跳到更快. 也就是单调栈的应用. 维持一个最大的栈底.
} //如果将要进来的元素大于此时的栈顶,就把栈顶删去. 直到为空. 然后把新加进来的元素放进去.
a[n+1] = inf;
top = 1;
pos[top] = n+1;
for(int i=n;i>=1;i--){
while(a[pos[top]] < a[i]) top--; //注意不要统计重了.
maxr[i] = pos[top];
pos[++top] = i;
}
ll res = 0;
for(int i=1;i<=n;i++) res += 1ll * a[i] * (i - maxl[i] ) * ( maxr[i] - i);
a[0] = -inf;
top = 1;
pos[top] = 0;
for(int i=1;i<=n;i++){
while(a[pos[top]] >= a[i]) top--;
minl[i] = pos[top];
pos[++top] = i;
}
a[n+1] = -inf;
top = 1;
pos[top] = n+1;
for(int i=n;i>=1;i--){
while(a[pos[top]] > a[i]) top--; //同样注意.
minr[i] = pos[top];
pos[++top] = i;
}
for(int i=1;i<=n;i++) res -= 1ll * a[i] * (i - minl[i] ) * ( minr[i] - i );
printf("%I64d\n",res);
}