|
|
#include <bits/stdc++.h>
|
|
|
|
|
|
using namespace std;
|
|
|
const int N = 1010;
|
|
|
const int INF = 0x3f3f3f3f;
|
|
|
|
|
|
int n; // n行
|
|
|
int m; // m列
|
|
|
int k; // 区间长度为k
|
|
|
int w[N][N]; // 原矩阵
|
|
|
|
|
|
// 暴力法,TLE
|
|
|
// 通过了 4/10个数据
|
|
|
|
|
|
/**
|
|
|
* 模板函数,需记忆
|
|
|
* 1、处理的是一维数组a[],保存的是一维数组b[]
|
|
|
* 2、之所以使用一维数组,其实本质上是把二维数组拆开每一行,当作一维数组进行处理
|
|
|
* 3、C++函数参数中传递一维数组,是不知道数组长度的,必须同时传输col,表示a[],b[]一共多少列
|
|
|
* 4、这个函数用句人话描述就是:把一个一维数组的长度为K区间内的最小值都算出来,放到row_min这个数组中去。比如:k=3,row_min[5]=2,就是表示原数组[3,4,5]下标内,最小值是2
|
|
|
*/
|
|
|
void get_min(int a[], int b[], int col) {
|
|
|
for (int i = 1; i <= col; i++) { // 枚举每一列
|
|
|
b[i] = INF;
|
|
|
// 找出每个数字包含[自己+前面]共k个范围内的最小值
|
|
|
// j的含义:指针,从i开始,向前倒k个 j∈[i-k+1,i]
|
|
|
// 举栗子:i=10,k=2,则应该是[9,10];如果i=1,k=2,则j只能取数值[1],即j>0
|
|
|
for (int j = i; j > max(i - k, 0); j--) b[i] = min(b[i], a[j]);
|
|
|
|
|
|
// 注:这个循环,还是倒序方便些,正序的反倒是代码长度更长
|
|
|
// for (int j = max(i - k + 1, 1); j <= i; j++) b[i] = min(b[i], a[j]);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void get_max(int a[], int b[], int col) {
|
|
|
for (int i = 1; i <= col; i++) { // 枚举每一列
|
|
|
b[i] = -INF;
|
|
|
for (int j = i; j > max(i - k, 0); j--) b[i] = max(b[i], a[j]);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
int main() {
|
|
|
cin >> n >> m >> k; // n*m矩阵,找出k*k的正方形区域
|
|
|
// 读入
|
|
|
for (int i = 1; i <= n; i++)
|
|
|
for (int j = 1; j <= m; j++)
|
|
|
cin >> w[i][j];
|
|
|
|
|
|
/*步骤I:遍历每一行,完成最小值、最大值的向右转储,分别记录到row_min、row_max两个数组中。
|
|
|
这两个数组,只是一个中间的状态,是为了给步骤II“竖着计算k个范围内的极大极小值”提供垫脚石
|
|
|
*/
|
|
|
int row_min[N][N]; // 最小值
|
|
|
int row_max[N][N]; // 最大值
|
|
|
for (int i = 1; i <= n; i++) {
|
|
|
get_min(w[i], row_min[i], m); // 填充每一行的,k个长度的区间内最小值 保存到row_min数组中, 注意:这并不是指某一个最小值,而是从k~m的所有长度够k个长度的区间极小值
|
|
|
get_max(w[i], row_max[i], m); // 填充每一行的,k个长度的区间内最大值 保存到row_max数组中, 注意:这并不是指某一个最大值,而是从k~m的所有长度够k个长度的区间极大值
|
|
|
}
|
|
|
|
|
|
int t[N]; // 列转行,用到的临时中转数组
|
|
|
int a[N]; // 最小值数组
|
|
|
int b[N]; // 最大值数组
|
|
|
|
|
|
/*
|
|
|
步骤II:将竖向的区间极值向右下角归并
|
|
|
(1)、依托row_min,对每一列进行列转行,保存为t
|
|
|
(2)、利用get_min 将t数组再次存储为a数组
|
|
|
(3)、此a数组,就是左归右,上归下的,边长为k的矩形中的最小值
|
|
|
|
|
|
(4)、依托row_max,对每一列进行列转行,保存为t
|
|
|
(5)、利用get_max 将t数组再次存储为b数组
|
|
|
(6)、此b数组,就是左归右,上归下的,边长为k的矩形中的最大值
|
|
|
*/
|
|
|
int res = INF; // 预求最小,先设最大
|
|
|
|
|
|
for (int j = k; j <= m; j++) { // 捋着列来
|
|
|
|
|
|
for (int i = 1; i <= n; i++) t[i] = row_min[i][j]; // 同一列的每一行,抄出来放到临时数组t中
|
|
|
get_min(t, a, n); // 对t这个临时数组,进行求k个范围内的最小值,将结果保存到a数组中
|
|
|
|
|
|
for (int i = 1; i <= n; i++) t[i] = row_max[i][j]; // 同一列的每一行,抄出来放到临时数组t中
|
|
|
get_max(t, b, n); // 对t这个临时数组,进行求k个范围内的最大值,将结果保存到b数组中
|
|
|
|
|
|
// 区域最大值-区域最小值,注意需要从k开始,前面的不够资格
|
|
|
for (int i = k; i <= n; i++) res = min(res, b[i] - a[i]);
|
|
|
}
|
|
|
// 输出
|
|
|
cout << res << endl;
|
|
|
return 0;
|
|
|
}
|