单调栈与单调队列

单调栈和单调队列

RMQ问题

问题引入:RMQ(x, y)就是询问数组【x, y】区间内部的最小值

如:RMQ(0, 3) = 1, RMQ(3, 7) = 2

数组arr【3, 1, 4 ,5 ,2, 9, 8, 12】;

分别指的是0 - 3下标中的最小值为 1, 3 - 7下标中的最小值为2

前缀和序列

设存在原序列,且 a 0 = 0 a_0 = 0 a0=0
a 0 , a 1 , a 2 , a 3 , . . . , a n − 1 , a n ; (1.1) a_0, a_1, a_2, a_3, ..., a_{n - 1}, a_n;\tag{1.1} a0,a1,a2,a3,...,an1,an;(1.1)

所谓前缀和序列指的是,前N项和等于前N - 1项和加上第N项的序列:
S n = S n − 1 + a n ; (1.2) S_n = S_{n - 1} + a_n; \tag{1.2} Sn=Sn1+an;(1.2)
即前缀和序列为:
S 1 , S 2 , S 3 , . . . , S n − 2 + a n − 1 , S n − 1 + a n ; (1.3) {S_1, S_2, S_3,..., S_{n - 2} + a_{n - 1}, S_{n - 1} + a_n};\tag{1.3} S1,S2,S3,...,Sn2+an1,Sn1+an;(1.3)
反过来也可以通过前缀和序列(3)求得原序列第N项的值:
a n = S n − S n − 1 ; (1.4) a_n = S_n - S_{n - 1};\tag{1.4} an=SnSn1;(1.4)
即由(4)可得到:
a 0 , a 1 , a 2 , a 3 , . . . , S n − 1 − S n − 2 , S n − S n − 1 等价于 ( 1.1 ) ; (1.5) a_0, a_1, a_2, a_3, ..., S_{n-1} - S_{n-2}, S_n - S_{n - 1} 等价于(1.1);\tag{1.5} a0,a1,a2,a3,...,Sn1Sn2,SnSn1等价于(1.1);(1.5)

差分序列

差分序列指的是,第N项与第N - 1项差的序列(设 a 0 = 0 a_0 = 0 a0=0):
X n = a n − a n − 1 ; (2.1) X_n = a_n - a_{n - 1};\tag{2.1} Xn=anan1;(2.1)
即:
X 1 , X 2 , X 3 , . . . X n − 1 , X n ; (2.2) X_1, X_2, X_3, ...X_{n-1}, X_n;\tag{2.2} X1,X2,X3,...Xn1,Xn;(2.2)

单调队列

维护一个以j位置为结尾的单调序列,就可以维护(i, j)的最值答案, 加上区间长度的限制,就是所谓的单调队列;

单调队列与普通队列的不同就在于为了维护队列的单调性,在新元素入队前会先判断队尾元素和待入队元素的大小关系是否与队列的单调性一致;一致就入队,否则从队尾淘汰老元素;同时如果队首元素超出了区间长度的范围,就会在队首执行出队操作;

单调栈

相对于单调队列而言,单调栈不需要进行过期判断,即队首元素出队操作,因此单调栈所维护的性质就是最近最值(大于|小于关系)问题;

练习
单调队列

在这里插入图片描述

#include<stdio.h>
#define MAX_N 300000
int arr[MAX_N + 5];
int que[MAX_N + 5];
int main() {
    int N, K;
    scanf("%d%d", &N, &K);
    for (int i = 1; i <= N; i++) scanf("%d", &arr[i]);
    //求最小值
    int head = 0, tail = 0;
    for (int i = 1; i <= N; i++) {
        while (tail - head && arr[i] < arr[que[tail - 1]]) tail--;
        que[tail++] = i;
        if (i < K) continue;
        i != K && printf(" ");
        if (i - que[head] >= K) head++;
        printf("%d", arr[que[head]]);
    }
    printf("\n");
    
    //求最大值
    head = 0, tail = 0;

    for (int i = 1; i <= N; i++) {
        while (tail - head && arr[i] > arr[que[tail - 1]]) tail--;
        que[tail++] = i;
        if (i < K) continue;
        i != K && printf(" ");
        if (i - que[head] >= K) head++;
        printf("%d", arr[que[head]]);
    }
    printf("\n");
    return 0;
}

在这里插入图片描述

#include<stdio.h>
#include<inttypes.h>
#define MAX_N 300000

int S[MAX_N + 5];
int que[MAX_N + 5], head = 0, tail = 0;
int ans = INT32_MIN;

int main() {
    int N, M;
    scanf("%d%d", &N, &M);
    for (int i = 1; i <= N; i++ ) {
        scanf("%d", &S[i]);
        S[i] += S[i - 1];
    }

    que[tail++] = 0;
    
    for (int i = 1; i <= N; i++) {
        if (i - que[head] > M) head++;
        ans = ans >= S[i] - S[que[head]] ? ans: S[i] - S[que[head]];
        while (tail - head && S[i] <= S[que[tail - 1]]) tail--;
        que[tail++] = i;
    }
    printf("%d\n", ans);
    return 0;
}

单调栈

在这里插入图片描述

#include<stdio.h>
#define MAX_N 100000

long long l[MAX_N + 5];
long long r[MAX_N + 5], stk[MAX_N + 5];
long long S[MAX_N + 5], ans = 0;
int main() {
    int N;
    scanf("%d", &N);
    for (int i = 1; i <= N; i++) {
        scanf("%lld", &S[i]);
    }
    l[0] = -1, l[N + 1] = -1;
    r[0] = -1, r[N + 1] = -1;
    int top = 0;
    stk[top = 0] = 0;
    //从左往右
    for (int i = 1; i <= N; i++) {
        while (S[stk[top]] >= S[i]) top--;
        l[i] = stk[top];
        stk[++top] = i;
    }
    stk[top = 0] = 0;
    //从右往左
    for (int i = N; i >= 1; i--) {
        while (S[stk[top]] >= S[i]) top--;
        r[i] = stk[top];
        stk[++top] = i;
    }

    //计算
    for (int i = 1; i <= N; i++) {
        ans = ans >= S[i] * (r[i] - l[i] - 1) ? ans : S[i] * (r[i] - l[i] - 1);
    }
    printf("%lld\n", ans);
    return 0;
}

在这里插入图片描述

#include<stdio.h>

#define MAX_N 500000
int N, P = 1;
int a[MAX_N + 5], b[MAX_N + 5];

int main() {
    scanf("%d", &N);
    for (int i = 0; i < N; i++) {
        scanf("%d", &a[i]);
    }
    for (int i = 0; i < N; i++) {
        scanf("%d", &b[i]);
    }
    int top1 = -1, top2 = -1;
    while (P < N) {
        while (top1 != -1 && a[top1] >= a[P]) top1--;
        while (top2 != -1 && b[top2] >= b[P]) top2--;
        if (top1 - top2) break;
        a[++top1] = a[P];
        b[++top2] = b[P];
        P++;
    }
    printf("%d\n", P);
    return 0;
}

  • 20
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值