[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;
}
posted @ 2021-02-26 14:10  lxzy  阅读(104)  评论(0)    收藏  举报