https://www.luogu.org/problem/P2216
有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小。
针对每一行维护一个单调队列(类似于维护一个滑动窗口),利用deque单调递增时维护该行当前滑动窗口的最小值的位置
(注意不是最小值),利用deque单调递减时维护该行当前滑动窗口的最大值的位置
(注意不是最大值)
然后吸吸氧就过了…
// luogu-judger-enable-o2
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define INF 0x3f3f3f3f
#define N 1010
using namespace std;
deque<int> Q[N],Q2[N];//分别维护给定滑动窗口的最小值和最大值的位置
int mz[N][N];
int mn[N][N],mx[N][N];//记录每一行给定位置的最大值与最小值
int a,b,n;
int main() {
// freopen("p.txt","r",stdin);
scanf("%d%d%d",&a,&b,&n);
for(int i=1; i<=a; i++) {
for(int j=1; j<=b; j++) {
scanf("%d",&mz[i][j]);
}
}
//单调队列维护下标
for(int i=1; i<=a; i++) {
for(int j=1; j<=b; j++) {
int now=mz[i][j];
while(!Q[i].empty() && now<=mz[i][Q[i].back()]) {//注意这里不是最小值的位置,而是最小值
Q[i].pop_back();
}
Q[i].push_back(j);//维护窗口里面每个值的位置索引
while(Q[i].back()-Q[i].front()+1>n) Q[i].pop_front();
if(j>=n) mn[i][j]=mz[i][Q[i].front()];
//下降
while(!Q2[i].empty() && now>=mz[i][Q2[i].back()]) Q2[i].pop_back();
Q2[i].push_back(j);
while(Q2[i].back()-Q2[i].front()+1>n) Q2[i].pop_front();
if(j>=n) mx[i][j]=mz[i][Q2[i].front()];
}
}
int ans=INF;
for(int j=n; j<=b; j++) {
for(int i=1; i+n-1<=a; i++) {
int mxv=-1,mnv=INF;
for(int d=0; d<n; d++) mxv=max(mxv,mx[i+d][j]),mnv=min(mnv,mn[i+d][j]);
ans=min(ans,mxv-mnv);
}
}
printf("%d\n",ans);
return 0;
}