BZOJ1047 [HAOI2007]理想的正方形
Description
有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值
的差最小。
Input
第一行为3个整数,分别表示a,b,n的值第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数。每
行相邻两数之间用一空格分隔。
100%的数据2<=a,b<=1000,n<=a,n<=b,n<=1000
Output
仅一个整数,为a*b矩阵中所有“n*n正方形区域中的最大整数和最小整数的差值”的最小值。
Sample Input
5 4 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2
Sample Output
1
题解
求以每个点为左上角的n*n的子矩阵中的最大值和最小值。
以最小值为例,枚举i,j,由于每次需要求每一列在第[i,i+n)行的最小值,而i是顺序枚举,所以每一列分别建单调队列,每次i++时所有单调队列向下滑动。
枚举j时,再建一个单调队列,值是所有列的单调队列的当前最小值。
最大值同理。代码中将矩阵每个数取反,然后求最小值。
附代码:
#include <algorithm> #include <cstdio> #include <deque> using std::deque; const int N = 1050; int A[N][N]; int _ans[2][N][N]; int a, b, n; struct MQ{ int v[N]; deque<int> DQ; int ttop, end; void clear() { DQ.clear(); ttop = end = 0; } void push(int x) { v[end] = x; while (!DQ.empty() && x < v[DQ.back()]) DQ.pop_back(); DQ.push_back(end++); } int top() { return v[DQ.front()]; } void pop() { if (DQ.front() == ttop++) DQ.pop_front(); } }; MQ C[N], R; void solve(int x) { int (*ans)[N] = _ans[x]; for (int i = 0; i < b; ++i) { C[i].clear(); for (int j = 0; j < n; ++j) C[i].push(A[j][i]); } for (int i = 0; i + n - 1 < a; ++i) { R.clear(); for (int j = 0; j < n; ++j) R.push(C[j].top()); for (int j = 0; j + n - 1 < b; ++j) { ans[i][j] = R.top(); R.pop(); if (j + n < b) R.push(C[j + n].top()); } for (int j = 0; j < b; ++j) { C[j].pop(); C[j].push(A[i + n][j]); } } } int main() { scanf("%d%d%d", &a, &b, &n); for (int i = 0; i < a; ++i) for (int j = 0; j < b; ++j) scanf("%d", &A[i][j]); solve(0); for (int i = 0; i < a; ++i) for (int j = 0; j < b; ++j) A[i][j] *= -1; solve(1); int ans = 1000000000; for (int i = 0; i + n - 1 < a; ++i) for (int j = 0; j + n - 1 < b; ++j) ans = std::min(ans, -(_ans[0][i][j] + _ans[1][i][j])); printf("%d\n", ans); return 0; }