P1886 滑动窗口 /【模板】单调队列

学习来源↓

题解 P1886 【滑动窗口】

性质:学习来源

  • 队列中的元素其对应在原来的列表中的顺序必须是单调递增的 - 说的是 for(1~n)
  • 队列中元素的大小必须是单调递*(增/减/甚至是自定义也可以)

单调队列与普通队列不一样的地方就在于:单调队列既可以从队首出队,也可以从队尾出队


在这里插入图片描述


单调队列板子

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N];
int n, m, k;

namespace Monotonous_queue { //单调队列板子
    int q[N];
    int h = 1, t = 0;//首 尾
    //需要的就那么几个东西
    
    int p[N];//编号
    
    void queryMax() {
        h = 1;
        t = 0; // 每次从队尾加入新的值

        for (int i = 1; i <= n; ++i) {
			//a[i]为当前处理的值
            while (h <= t && q[t] <= a[i]) t--;
            //剔除非最大值的值 此时剩下的就是比a[i]更大的值
            
            q[++t] = a[i]; //把新的值加入进来
            p[t] = i;
            while (p[h] + k <= i) h++;//缩小范围

            if (i >= k) {
                cout << q[h] << ' ';//每次窗口间的最大值
            }
        }
        cout << endl;
    }

    void queryMin() {
        h = 1;
        t = 0;

        for (int i = 1; i <= n; ++i) {
            //a[i]为当前处理的值
            while (h <= t && q[t] >= a[i]) t--;
            q[++t] = a[i];
            p[t] = i;
            while (p[h] + k <= i) h++;
            
            if (i >= k) { //区间个数要等于k 那么肯定是处理到第k个开始才会有值输出
                cout << q[h] << ' ';
            }
        }
        cout << endl;
    }

}
using namespace Monotonous_queue;

int main() {
    ios::sync_with_stdio(0);
    cin >> n >> k;//n个数 窗口大小为k
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }

    queryMin();
    queryMax();
    return 0;
}

二维单调队列板子

P2216 [HAOI2007]理想的正方形

#include <bits/stdc++.h>
using namespace std;

namespace MonotonousQueue2 {//二维单调队列
    const int N = 1005;// 当N=5000时 危 · 抠细节
    typedef long long ll;
    int head1, tail1, head2, tail2;
    int qMax[N][N], qMin[N][N];
    int a[N][N];

    struct node {
        int v;//val
        int id;
    } q1[N], q2[N];//q1 维护纵向 q2 维护横向

    void addMax(int v, int id) {//维护队列最大值
        while (head1 < tail1 && q1[tail1 - 1].v <= v) tail1--;
        q1[tail1++] = {v, id};
    }

    int getMaxNum(int id) {
        while (head1 < tail1 && q1[head1].id < id) head1++;
        return q1[head1].v;
    }

    void addMin(int v, int id) {//维护队列最小值
        while (head2 < tail2 && q2[tail2 - 1].v >= v) tail2--;
        q2[tail2++] = {v, id};
    }

    int getMinNum(int id) {
        while (head2 < tail2 && q2[head2].id < id) head2++;
        return q2[head2].v;
    }


    //在 n*m 的网格里 找 r*c的矩阵 里的最大值和最小值 的差值
    void MQ2(int n, int m, int r, int c) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                scanf("%d", &a[i][j]);
            }
        }

        for (int j = 0; j < m; j++) {
            head1 = tail1 = head2 = tail2 = 0;
            for (int i = 0; i < r - 1; i++) {
                addMax(a[i][j], i);
                addMin(a[i][j], i);
            }

            for (int i = r - 1; i < n; i++) {
                addMax(a[i][j], i);
                // 这里qMax[i][j]表示第j列, max( matrix[ i->(i+r) ][j] )中的最大值
                qMax[i - r + 1][j] = getMaxNum(i - r + 1);
                addMin(a[i][j], i);
                qMin[i - r + 1][j] = getMinNum(i - r + 1);
            }
        }

        for (int i = 0; i < n - r + 1; i++) {
            head1 = tail1 = head2 = tail2 = 0;
            for (int j = 0; j < c - 1; j++) {
                addMax(qMax[i][j], j);
                addMin(qMin[i][j], j);
            }
            for (int j = c - 1; j < m; j++) {
                addMax(qMax[i][j], j);
                qMax[i][j - c + 1] = getMaxNum(j - c + 1);
                addMin(qMin[i][j], j);
                qMin[i][j - c + 1] = getMinNum(j - c + 1);
            }
        }

        int res = qMax[0][0] - qMin[0][0];
        for (int i = 0; i < n - r + 1; i++) {
            for (int j = 0; j < m - c + 1; j++) {
                res = min(res, qMax[i][j] - qMin[i][j]);
            }
        }
        printf("%d\n", res);
    }

}
using namespace MonotonousQueue2;

int main() {
    ios::sync_with_stdio(false);
    
    int a, b, n;
    while (~scanf("%d%d%d", &a, &b, &n))
        MQ2(a, b, n, n);
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值