[BZOJ 1047] [HAOI2007] 理想的正方形 【单调队列】
题目链接:BZOJ - 1047
题目分析
使用单调队列在 O(n^2) 的时间内求出每个 n * n 正方形的最大值,最小值。然后就可以直接统计答案了。
横向有 a 个单调队列(代码中是 Q[1] 到 Q[a] ),维护每行当前枚举区间的单调队列。
纵向一个单调队列(代码中是 Q[0] ),求出当前枚举区间的每行的单调队列后,就得到了每行的这个区间的最小值(最大值),就相当于一个长度为行数的数组,然后纵向做单调队列,求出的就是正方形的最值了。
代码
#include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; const int MaxN = 1000 + 5, INF = 999999999; int a, b, n, Ans; int Map[MaxN][MaxN], Q[MaxN][MaxN], F[MaxN], Head[MaxN], Tail[MaxN], Min[MaxN][MaxN], Max[MaxN][MaxN]; //Q[0]是纵向的单调队列 inline int gmin(int a, int b) {return a < b ? a : b;} inline int gmax(int a, int b) {return a > b ? a : b;} void Get_Min() { for (int i = 1; i <= a; ++i) { Head[i] = 1; Tail[i] = 0; } for (int i = 1; i <= b; ++i) { for (int j = 1; j <= a; ++j) { if (i > n && Head[j] <= Tail[j] && Q[j][Head[j]] == i - n) ++Head[j]; while (Head[j] <= Tail[j] && Map[j][i] < Map[j][Q[j][Tail[j]]]) --Tail[j]; Q[j][++Tail[j]] = i; } if (i >= n) { Head[0] = 1; Tail[0] = 0; for (int j = 1; j <= a; ++j) { F[j] = Map[j][Q[j][Head[j]]]; if (j > n && Head[0] <= Tail[0] && Q[0][Head[0]] == j - n) ++Head[0]; while (Head[0] <= Tail[0] && F[j] < F[Q[0][Tail[0]]]) --Tail[0]; Q[0][++Tail[0]] = j; if (j >= n) Min[j][i] = F[Q[0][Head[0]]]; } } } } void Get_Max() { for (int i = 1; i <= a; ++i) { Head[i] = 1; Tail[i] = 0; } for (int i = 1; i <= b; ++i) { for (int j = 1; j <= a; ++j) { if (i > n && Head[j] <= Tail[j] && Q[j][Head[j]] == i - n) ++Head[j]; while (Head[j] <= Tail[j] && Map[j][i] > Map[j][Q[j][Tail[j]]]) --Tail[j]; Q[j][++Tail[j]] = i; } if (i >= n) { Head[0] = 1; Tail[0] = 0; for (int j = 1; j <= a; ++j) { F[j] = Map[j][Q[j][Head[j]]]; if (j > n && Head[0] <= Tail[0] && Q[0][Head[0]] == j - n) ++Head[0]; while (Head[0] <= Tail[0] && F[j] > F[Q[0][Tail[0]]]) --Tail[0]; Q[0][++Tail[0]] = j; if (j >= n) Max[j][i] = F[Q[0][Head[0]]]; } } } } int main() { scanf("%d%d%d", &a, &b, &n); for (int i = 1; i <= a; ++i) for (int j = 1; j <= b; ++j) scanf("%d", &Map[i][j]); Get_Min(); Get_Max(); Ans = INF; for (int i = n; i <= a; ++i) for (int j = n; j <= b; ++j) Ans = gmin(Ans, Max[i][j] - Min[i][j]); printf("%d\n", Ans); return 0; }