HAOI 2007 理想的正方形 题解

题目传送门

题目大意: 给出一个矩阵,找出里面最大值与最小值的差值最小的 n × n n \times n n×n 大小的矩阵。

题解

首先考虑暴力,枚举每个 n × n n \times n n×n 的矩阵,时间复杂度 O ( a × b × n 2 ) O(a \times b \times n^2) O(a×b×n2)

显然我们只能承受 O ( a × b ) O(a \times b) O(a×b) 的时间复杂度,于是考虑优化掉那个 n 2 n^2 n2 的部分。

发现我们可以预处理两个数组: m a [ i ] [ j ] ma[i][j] ma[i][j] m i [ i ] [ j ] mi[i][j] mi[i][j],分别表示第 i i i 行从第 j j j 个开始往前的 n n n 个数字中的最大值和最小值。这个只需要用单调队列扫一遍就可以了。

发现预处理完这个东西之后可以利用它竖着跑单调队列,每次加进来的 n n n 中的最大最小值我们已经知道了,只需要 O ( 1 ) O(1) O(1) 读取即可,然后顺便更新一下答案就可以了。

代码如下:

#include <cstdio>
#define maxn 1010

int a,b,n;
int ma[maxn][maxn],mi[maxn][maxn];
struct queue_{
	int q[maxn],time[maxn],st,ed;
	void reset(){st=1;ed=0;}
}q1,q2;
int min(int x,int y){return x<y?x:y;}

int main()
{
	scanf("%d %d %d",&a,&b,&n);
	for(int i=1;i<=a;i++)
	{
		q1.reset();q2.reset();
		for(int j=1;j<=b;j++)
		{
			int x; scanf("%d",&x);
			while(q1.ed>=q1.st&&q1.q[q1.ed]<x)q1.ed--; while(q1.st<=q1.ed&&q1.time[q1.st]<j-n+1)q1.st++;
			q1.q[++q1.ed]=x; q1.time[q1.ed]=j;
			
			while(q2.ed>=q2.st&&q2.q[q2.ed]>x)q2.ed--; while(q2.st<=q2.ed&&q2.time[q2.st]<j-n+1)q2.st++;
			q2.q[++q2.ed]=x; q2.time[q2.ed]=j;
			ma[i][j]=q1.q[q1.st],mi[i][j]=q2.q[q2.st];
		}
	}
	int ans=2147483640;
	for(int j=n;j<=b;j++)
	{
		q1.reset();q2.reset();
		for(int i=1;i<=a;i++)
		{
			while(q1.ed>=q1.st&&q1.q[q1.ed]<ma[i][j])q1.ed--; while(q1.st<=q1.ed&&q1.time[q1.st]<i-n+1)q1.st++;
			q1.q[++q1.ed]=ma[i][j]; q1.time[q1.ed]=i;
			
			while(q2.ed>=q2.st&&q2.q[q2.ed]>mi[i][j])q2.ed--; while(q2.st<=q2.ed&&q2.time[q2.st]<i-n+1)q2.st++;
			q2.q[++q2.ed]=mi[i][j]; q2.time[q2.ed]=i;
			if(i>=n&&q1.q[q1.st]-q2.q[q2.st]<ans)ans=q1.q[q1.st]-q2.q[q2.st];
		}
	}
	printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值