P2216 [HAOI2007]理想的正方形
题目描述
有一个 \(a \times b\) 的整数组成的矩阵,现请你从中找出一个 \(n \times n\)的正方形区域,使得该区域所有数中的最大值和最小值的差最小。
输入格式
第一行为 \(3\) 个整数,分别表示 \(a,b\) 的值。
第二行至第 \(a+1\) 行每行为 \(b\) 个非负整数,表示矩阵中相应位置上的数。每行相邻两数之间用一空格分隔。
输出格式
仅一个整数,为 \(a \times b\) 矩阵中所有“ \(n \times n\) 正方形区域中的最大整数和最小整数的差值”的最小值。
输入输出样例
输入 #1
5 4 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2
输出 #1
1
Solution
这个题很裸,只要能 \(O(abn)\) 及以内地查询二维区间最值的算法都可以过。
数据也不卡你,然后实际上简单优化一下的暴力就能过。
然后像什么ST表啊、RMQ、单调队列啊甚至线段树也都可以过。
思路很简单,先一遍求区间最值算法求出每列(或行)每个长度为 \(n\) 的区间的最大·小值,然后对每行(或列)用之前算出的最大·小值再做一次,求出每个 长度为 \(n\) 的区间内的 \(n\) 维列(或行)向量中数的最大值的最大值和最小值的最小值相减,然后取最小值即可。
我写的单调队列做法, \(O(ab)\) 复杂度。
但是,但是,STL 的 deque 的常数怎么可以这么大???吸个氧速度快了6倍还多QAQ
Code
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<deque>
#define N 1010
#define min(a,b) (a) < (b) ? (a) : (b)
#define max(a,b) (a) > (b) ? (a) : (b)
#define LL long long
using namespace std;
inline int read()
{
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') {x = (x << 1) + (x << 3) + (c ^ 48); c=getchar();}
return x * f;
}
struct number{
LL num, index;
};
int main()
{
int a = read(), b = read(), n = read();
LL mp[N][N], cmax[N][N], cmin[N][N];
LL ans = 1e10 + 1;
deque<number> cmaxq, cminq;
for(int i=1;i<=a;i++)
for(int j=1;j<=b;j++)
mp[i][j] = read();
for(int c=1;c<=b;c++){ //预处理每列区间最大、小值
cmaxq.clear(), cminq.clear();
for(int r=1;r<=a;r++) {
struct number nw = {mp[r][c], r};
while(cminq.size() && cminq.back().num >= mp[r][c])
cminq.pop_back();
cminq.push_back(nw);
while(r - cminq.front().index + 1 > n) cminq.pop_front();
if(r >= n) cmin[r - n + 1][c] = cminq.front().num;
while(cmaxq.size() && cmaxq.back().num <= mp[r][c])
cmaxq.pop_back();
cmaxq.push_back(nw);
while(r - cmaxq.front().index + 1 > n) cmaxq.pop_front();
if(r >= n) cmax[r - n + 1][c] = cmaxq.front().num;
}
}
for(int r=1;r<=a-n+1;r++) {
cmaxq.clear(), cminq.clear();
for(int c=1;c<=b;c++) { //处理所有以(r, c)为左上角的矩形
struct number nwmin = {cmin[r][c], c};
struct number nwmax = {cmax[r][c], c};
LL tmmax, tmmin;
while(cminq.size() && cminq.back().num >= cmin[r][c])
cminq.pop_back();
cminq.push_back(nwmin);
while(c - cminq.front().index + 1 > n) cminq.pop_front();
if(c >= n) tmmin = cminq.front().num;
while(cmaxq.size() && cmaxq.back().num <= cmax[r][c])
cmaxq.pop_back();
cmaxq.push_back(nwmax);
while(c - cmaxq.front().index + 1 > n) cmaxq.pop_front();
if(c >= n) tmmax = cmaxq.front().num;
if(c >= n) ans = min(ans, tmmax - tmmin);
}
}
printf("%lld\n", ans);
return 0;
}