二维单调队列

单调队列通常用来解决区间最值问题。

二维单调队列用来在矩阵中找子矩阵的最值,即求a * b大小的子矩阵中的最大值与最小值。

做法: 我们先预处理出每行滑动窗口长度为b的最值,并将其放到窗口最右侧位置;如0~b - 1窗口的最值放到 下标为b - 1 的位置。

  处理完行后,我们对列进行处理,维护长度为a的滑动窗口,并将最值放到最下侧。  

  

 操作完后的子数组最值就在最右下角处。

 

 

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int N = 1010;

int n, m, k;
int w[N][N];
int row_min[N][N], row_max[N][N];
int q[N];

void get_min(int a[], int b[], int tot) {
    int hh = 0, tt = -1;
    for(int i = 1; i <= tot; i ++ ) {
        while(hh <= tt && q[hh] < i - k + 1) hh ++ ;
        while(hh <= tt && a[q[tt]] >= a[i]) tt -- ;
        q[ ++ tt] = i;
        b[i] = a[q[hh]];
    }
}

void get_max(int a[], int b[], int tot) {
    int hh = 0, tt = -1;
    for(int i = 1; i <= tot; i ++ ) {
        while(hh <= tt && q[hh] < i - k + 1) hh ++ ;
        while(hh <= tt && a[q[tt]] <= a[i]) tt -- ;
        q[ ++ tt] = i;
        b[i] = a[q[hh]];
    }
}

int main() {
    cin >> n >> m >> k;
    
    for(int i = 1; i <= n; i ++ ) {
        for(int j = 1; j <= m; j ++ ) {
            scanf("%d", &w[i][j]);
        }
    }
    
    for(int i = 1; i <= n; i ++ ) {
        get_min(w[i], row_min[i], m);
        get_max(w[i], row_max[i], m);
    }
    
    int res = 1e9;
    int a[N], b[N], c[N];
    for(int i = k; i <= m; i ++ ) {
        for(int j = 1; j <= n; j ++ ) a[j] = row_min[j][i];
        get_min(a, b, n);
        
        for(int j = 1; j <= n; j ++ ) a[j] = row_max[j][i];
        get_max(a, c, n);
        
        for(int j = k; j <= n; j ++ ) res = min(res, c[j] - b[j]);
    }
    
    cout << res << endl;
    
    return 0;
}

 

 

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

typedef long long LL;

const int N = 1010, MOD = 998244353;

int n, m, A, B;
int w[N][N];
int row_max[N][N], row_min[N][N];
int q[N];

void get_min(int a[], int b[], int tot, int k) {
    int hh = 0, tt = -1;
    for(int i = 1; i <= tot; i ++ ) {
        while(hh <= tt && q[hh] < i - k + 1) hh ++ ;
        while(hh <= tt && a[q[tt]] >= a[i]) tt -- ;
        q[ ++ tt] = i;
        b[i] = a[q[hh]];
    }
}

void get_max(int a[], int b[], int tot, int k) {
    int hh = 0, tt = -1;
    for(int i = 1; i <= tot; i ++ ) {
        while(hh <= tt && q[hh] < i - k + 1) hh ++ ;
        while(hh <= tt && a[q[tt]] <= a[i]) tt -- ;
        q[ ++ tt] = i;
        b[i] = a[q[hh]];
    }
}

int main() {
    scanf("%d%d%d%d", &n, &m, &A, &B);
    
    for(int i = 1; i <= n; i ++ ) {
        for(int j = 1; j <= m; j ++ ) {
            scanf("%d", &w[i][j]);
        }
    }
    
    for(int i = 1; i <= n; i ++ ) {
        get_min(w[i], row_min[i], m, B);
        get_max(w[i], row_max[i], m, B);
    }
    
    LL res = 0;
    int a[N], b[N], c[N];
    for(int i = B; i <= m; i ++ ) {
        for(int j = 1; j <= n; j ++ ) a[j] = row_max[j][i];
        get_max(a, b, n, A);
        
        for(int j = 1; j <= n; j ++ ) a[j] = row_min[j][i];
        get_min(a, c, n, A);
        
        for(int j = A; j <= n; j ++ ) res = (res + (LL)b[j] * c[j]) % MOD;
    }
    
    printf("%lld\n", res);
    
    return 0;
}

 

posted @ 2023-09-16 11:44  深渊之巅  阅读(202)  评论(0编辑  收藏  举报