题目链接

单调队列

题意:求最大子矩阵,矩阵中的最大值减去最小值 <= m

思路:
枚举上界下界和右界,左界通过单调队列在O(n)的时间内求出。

单调队列维护最小值和最大值不难,难点是维护 最大值 - 最小值 <= m
这里的维护方式是用一个变量 left 表示当前的左界,假设当前定的左界到右界的 最大值 - 最小值 > m,那么我们必须
增加区间最小值或者减小区间最大值才有可能让 最大值 - 最小值 <= m
只要不满足 最大值 - 最小值 <= m,就让 left 右移,尽可能的让区间最大值减小,区间最小值增大。

#pragma GCC optimize(2)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
#include <cmath>
   
using namespace std;
   
typedef long long ll;
   
const int MaxnN = 500+10;
const int MaxnM = 1e5+10;
const int INF = 0x3f3f3f3f;
   
int G[MaxnN][MaxnN];
int minx[MaxnN], maxn[MaxnN];
   
int qu_max[MaxnN], qu_min[MaxnN];
   
int main(void)
   
{
    int T;
    scanf("%d", &T);
    while(T--) {
        int n, m;
        scanf("%d%d", &n, &m);
        int ans = 1;
       
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j <= n; ++j) scanf("%d", &G[i][j]);
        }
           
        for(int i = 1; i <= n; ++i) {
            for(int k = 1; k <= n; ++k) {
                minx[k] = maxn[k] = G[i][k];
            }
            for(int j = i; j <= n; ++j) {
                // 逐步维护列最小和最大值
                for(int k = 1; k <= n; ++k) {
                    minx[k] = min(minx[k], G[j][k]);
                    maxn[k] = max(maxn[k], G[j][k]);
                }
                int h_max, t_max, h_min, t_min, left = 1;
                h_max = t_max = h_min = t_min = 0;
                for(int k = 1; k <= n; ++k) {
                   while(h_max < t_max && maxn[qu_max[t_max-1]] < maxn[k]) t_max--;
                   while(h_min < t_min && minx[qu_min[t_min-1]] > minx[k]) t_min--;
                   qu_max[t_max++] = k;
                   qu_min[t_min++] = k;
                     
                   while(left <= k && (h_max < t_max) && (h_min < t_min) && maxn[qu_max[h_max]]-minx[qu_min[h_min]] > m) {
                        left++;
                        while(h_max < t_max && qu_max[h_max] < left) h_max++;
                        while(h_min < t_min && qu_min[h_min] < left) h_min++;
                   }
                    if(left <= k) ans = max(ans, (j-i+1)*(k-left+1));
                }
            }
        }
        printf("%d\n", ans);
    }
    return 0;
 }