单调队列
题意:求最大子矩阵,矩阵中的最大值减去最小值 <= 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;
}
浙公网安备 33010602011771号