Description
有一个 \(a \times b\) 的整数组成的矩阵, 现请你从中找出一个 \(n \times n\) 的正方形 区域, 使得该区域所有数中的最大值和最小值的差最小.
Input
第一行为 \(3\) 个整数, 分别表示 \(a, b, n\) 的值第二行至第 \(a + 1\) 行每行为 \(b\) 个非负整数, 表示矩阵中相应位置上的数. 每行相邻两数之间用一空格分隔.
Output
仅一个整数, 为 \(a \times b\) 矩阵中所有 “\(n \times 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
Sample Output
1
Data Range
对于 100% 的数据, 2 a, b 1000, n a, n b, n 100 $
Explanation
可以用类似 DP 的方法来解这道题, 就是先维护每一行上一段区间的最大值和最小值, 然后 再接着维护一个矩阵内的最大值和最小值. 与求和不一样, 求和具有连续性, 而这个没有. 我们可以考虑建 \(2n\) 颗线段树来维护这些数据, 然后每一次查询, 但是实际并不需要这样 麻烦. 只需要用一个类似单调队列 (支持弹出) 的 STL 容器就可以了.
然后就可以水过这道题了, 时间复杂度 \(O(a \cdot b \cdot \log (a \cdot b))\), 空间 复杂度 \(O(a \cdot b)\), 并且取决于 STL 的空间复杂度实现.
又一次 1A 好开心~
Source Code
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <map>
#define rep(_var,_begin,_end) for(int _var=_begin;_var<=_end;_var++)
#define range(_begin,_end) rep(_,_begin,_end)
using namespace std;
typedef long long lli;
const int maxn = 1010;
const int infinit = 0x7f7f7f7f;
int a, b, n;
int arr[maxn][maxn];
int dp_row[maxn][maxn][2], // max, min [sub-rows]
dp_col[maxn][maxn][2]; // max, min [matrices]
void decrement(map<int, int>& _map, int val) {
_map[val]--; if (_map[val] <= 0)
_map.erase(_map.find(val)); return ; }
void increment(map<int, int>& _map, int val) {
_map[val]++; return ; }
int min_in(map<int, int>& _map) {
map<int, int>::iterator itert = _map.begin();
return itert->first; }
int max_in(map<int, int>& _map) {
map<int, int>::iterator itert = _map.end();
itert--; return itert->first; }
int main(int argc, char** argv)
{
scanf("%d%d%d", &a, &b, &n);
for (int i = 1; i <= a; i++)
for (int j = 1; j <= b; j++)
scanf("%d", &arr[i][j]);
// Processing maximum & minimum values on each row
for (int i = 1; i <= a; i++) {
map<int, int> mp;
increment(mp, 0); // Fail-safe (easier-to-code tweak) addition
for (int j = 1; j <= n - 1; j++)
increment(mp, arr[i][j]);
for (int j = 1; j + n - 1 <= b; j++) {
// Shift forward, by one position
decrement(mp, arr[i][j - 1]);
increment(mp, arr[i][j + n - 1]);
// Update max, minimum values
dp_row[i][j][0] = max_in(mp);
dp_row[i][j][1] = min_in(mp);
}
}
// Processing maximum & minimum values through columns (therefore matrices)
for (int j = 1; j + n - 1 <= b; j++) {
map<int, int> mp[2];
range(0,1) increment(mp[_], 0); // Fail-safe (easier-to-code tweak) additionb
for (int i = 1; i <= n - 1; i++)
range(0,1) increment(mp[_], dp_row[i][j][_]);
for (int i = 1; i + n - 1 <= a; i++) {
// Shift forward, by one position
range(0,1) decrement(mp[_], dp_row[i - 1][j][_]),
increment(mp[_], dp_row[i + n - 1][j][_]);
// Update max, minimum values
dp_col[i][j][0] = max_in(mp[0]);
dp_col[i][j][1] = min_in(mp[1]);
}
}
int best_diff = infinit;
for (int i = 1; i + n - 1 <= a; i++)
for (int j = 1; j + n - 1 <= b; j++)
best_diff = min(best_diff, dp_col[i][j][0] - dp_col[i][j][1]);
printf("%d\n", best_diff);
return 0;
}