Files
python/TangDou/AcWing/MonotonicQueue/1091_BruteForce.cpp
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

90 lines
4.2 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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;
}