[HAOI2007]理想的正方形
做题时间:2021.02.27
\(【题目描述】\)
给定一个\(N \times M(N,M \leq 2000)\)的矩阵,给定参数\(x\),求出在所有大小为\(x \times x\)的矩阵中,最大值与最小值差值最小的那个矩阵的差值。
\(【输入样例】\)
5 4 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2
\(【输出样例】\)
1
\(【考点】\)
单调队列
\(【做法】\)
题目可以简单理解为二维的 求连续区间最值(滑动窗口)
由于目标矩阵的大小固定,可以考虑对于列与行分别使用单调队列维护,具体来讲,首先使用单调队列求出每一行所有的\((j,j+x-1)\)区间内的最值,分别储存在两个大小为\(n\times (m-x+1)\)的矩阵\(mine\)和\(maxn\)内,这个矩阵\((i,j)\)位置上的数表示原数组中\([(i,j),(i,j+x-1)]\)区间的最值。
此时在\(mine\)和\(maxn\)数组内,相当于将每一个目标矩阵的列进行了压缩。
此时再次以\(mine\)和\(maxn\)数组为基准,对于每一列,求所有\((i,i+x-1)\)区间内的最值,分别存在两个答案数组\(ans1\)和\(ans2\)中。
此时\(ans1\)和\(ans2\)中就已经储存了整个原数组中所有目标矩阵的最大值和最小值,求出它们两两对应差值的最小值即可。
时间复杂度:\(O(NM)\)
\(【代码】\)
#include<cstdio>
#include<iomanip>
#define INF 0x7f7f7f7f
using namespace std;
const int N=1e3+50;
struct data{
int num,pos;
}que1[N],que2[N];
int l1,r1,l2,r2;
int a[N][N],maxnl[N][N],maxnr[N][N];
int minel[N][N],miner[N][N];
int n,m,x;
inline int Min(int p,int q){return p<q?p:q;}
void Push(int s,int p,int k)
{
if(k==1){
while(l1<r1&&que1[r1].num<s) r1--;
que1[++r1]=(data){s,p};
}
if(k==2){
while(l2<r2&&que2[r2].num<s) r2--;
que2[++r2]=(data){s,p};
}
}
void Pop(int k)
{
if(k==1&&l1<r1) l1++;
if(k==2&&l2<r2) l2++;
}
int main()
{
scanf("%d%d%d",&n,&m,&x);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) scanf("%d",&a[i][j]);
}
for(int i=1;i<=n;i++){
int tot=0;
l1=0,l2=0,r1=0,r2=0;
for(int j=1;j<=x;j++) Push(a[i][j],j,1),Push(a[i][j],j,1);
maxnl[i][++tot]=que1[l1+1].num;
minel[i][tot]=que2[l2+1].num;
for(int j=x+1;j<=m;j++){
if(que1[l1+1].pos<=j-x) Pop(1);
if(que2[l2+1].pos<=j-x) Pop(2);
Push(a[i][j],j,1),Push(a[i][j],j,2);
maxnl[i][++tot]=que1[l1+1].num;
minel[i][tot]=que2[l2+1].num;
}
}
for(int j=1;j<=m-x+1;j++){
int tot=0;
int l1=0,l2=0,r1=0,r2=0;
for(int i=1;i<=x;i++) Push(maxnl[i][j],i,1),Push(minel[i][j],i,2);
maxnr[j][++tot]=que1[l1+1].num;
miner[j][++tot]=que2[l2+1].num;
for(int i=x+1;i<=n;i++){
if(que1[l1+1].pos<=i-x) Pop(1);
if(que2[l2+1].pos<=i-x) Pop(2);
Push(maxnl[i][j],i,1),Push(minel[i][j],i,2);
maxnr[j][++tot]=que1[l1+1].num;
miner[j][++tot]=que2[l2+1].num;
}
}
int ans=INF;
for(int i=1;i<=n-x+1;i++){
for(int j=1;j<=n-x+1;j++) ans=Min(ans,maxnr[i][j]-miner[i][j]);
}
printf("%d\n",ans);
return 0;
}

浙公网安备 33010602011771号