题目链接:
https://ac.nowcoder.com/acm/contest/883/F
题意:
给一个N行N列的矩阵和一个数M,求最大的子矩阵使得该矩阵中最大值和最小值之差不超过M。
类似题目:
洛谷P2216 [HAOI2007]理想的正方形
洛谷P2219 [HAOI2007]修筑绿化带
codeforces Hello 2015 (Div.1) C题 Subrect Query (和本题几乎一样,求的东西不一样)
代码:
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define pi 3.1415926
#define mp(x, y) make_pair(x, y)
#define vi vector<int>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> Pair;
const int MAX = 5e2 + 5;
int T, N, M;
int grap[MAX][MAX], Mx[MAX], Mn[MAX], q1[MAX], q2[MAX];
//q1维护最小值递增队列
//q2维护最大值递减队列
//均记录位置
int main() {
#ifdef ACM_LOCAL
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
scanf("%d", &T);
while (T--) {
scanf("%d%d", &N, &M);
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++)
scanf("%d", &grap[i][j]);
int ans = 0;
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++)
Mx[j] = Mn[j] = grap[i][j];
for (int j = i; j <= N; j++) {
//Mx[k]为[i,j]行k列的最大值,Mn[k]为[i,j]行k列的最小值
for (int k = 1; k <= N; k++)
Mx[k] = max(Mx[k], grap[j][k]), Mn[k] = min(Mn[k], grap[j][k]);
if ((j - i + 1) * N <= ans)continue;//剪枝操作:(j - i + 1)行N列矩阵满足情况下都比已知答案小
int h1 = 1, h2 = 1, t1 = 0, t2 = 0;//队列head 和 tail
for (int l = 1, r = 1; r <= N; r++) {
while (h1 <= t1 && Mn[q1[t1]] >= Mn[r])t1--;//当前值 比递增队列 队尾 更小
q1[++t1] = r;
while (h2 <= t2 && Mx[q2[t2]] <= Mx[r])t2--;//当前值 比递减队列 队尾 更大
q2[++t2] = r;
while (l <= r && Mx[q2[h2]] - Mn[q1[h1]] > M) {//如果 区间内 最值之差不满足条件,说明区间过长了
//为什么能代表整个子矩阵呢,因为当前行的值是最值(在前几行的基础上取最值得来),而且队列是单调的,队头最大/最小,所以队头的值就是当前整个子矩阵的最值
l++;
while (h1 <= t1 && q1[h1] < l)h1++;//保证队头在区间内
while (h2 <= t2 && q2[h2] < l)h2++;//保证队头在区间内
}
ans = max(ans, (j - i + 1) * (r - l + 1));// (j - i + 1)就是矩阵的行数, (r - l + 1)就是矩阵的列数
if ((j - i + 1) * (N - l + 1) <= ans)break;//剪纸条件: 剩下的也不能再超过已知最大值
}
}
}
printf("%d\n", ans);
}
return 0;
}