BZOJ1047

1047: [HAOI2007]理想的正方形

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 1587   Solved: 835
[ Submit][ Status]

Description

有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小。

Input

第一行为3个整数,分别表示a,b,n的值第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数。每行相邻两数之间用一空格分隔。

Output

仅一个整数,为a*b矩阵中所有“n*n正方形区域中的最大整数和最小整数的差值”的最小值。

Sample Input

5 4 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2

Sample Output

1

HINT

问题规模

(1)矩阵中的所有数都不超过1,000,000,000

(2)20%的数据2<=a,b<=100,n<=a,n<=b,n<=10

(3)100%的数据2<=a,b<=1000,n<=a,n<=b,n<=10

Source




[题解] 这道题,最初我想卡,可惜失败了;

一开始,我想,可以 枚举行O(a),然后用2*n个单调队列维护 以这一行为底的大每一列的最最小值,但是觉得这样做有许多是重复做的工作,也很容易TLE,所以就进一步思考。

我们考虑得到 i, j ,求出以(i,j)为右下角的n*n的矩阵的最大最小值。这也可以用单调队列实现,具体可以看代码。


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxnn 1800
#define inf 2147483641

int a,b,n,ll[maxnn],rr[maxnn],f[maxnn],w[maxnn][maxnn],h[maxnn][maxnn];
int Min[maxnn][maxnn],Max[maxnn][maxnn],q[maxnn][maxnn],ans;

inline void read(int &x)
{
	x=0;char ch;int ff=1;
	while(ch=getchar(),(ch>'9' || ch<'0')&& ch!='-');
	if(ch=='-')ff=-1,ch=getchar();
	while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();
	x*=ff;
}
inline void getMin()
{
	for(int i=1;i<=b;i++)ll[i]=1,rr[i]=0;
	for(int i=1,l,r;i<=a;i++)
	{
		l=1;r=f[0]=0;
		for(int j=1;j<=b;j++)
		{
			while(l<=r && f[l]+n<=j)l++;
			while(l<=r && w[i][j]<w[i][f[r]])r--;
			f[++r]=j; q[i][j]=f[l];
		}
		for(int j=1;j<=b;j++)
		{
			while(ll[j]<=rr[j] && h[j][ll[j]]+n<=i)ll[j]++;
			while(ll[j]<=rr[j] && w[h[j][rr[j]]][q[h[j][rr[j]]][j]]>w[i][q[i][j]])rr[j]--;
			h[j][++rr[j]]=i;
			Min[i][j]=w[h[j][ll[j]]][q[h[j][ll[j]]][j]];
		}
	}
}
inline void getMax()
{
	for(int i=1;i<=b;i++)ll[i]=1,rr[i]=0;
	for(int i=1,l,r;i<=a;i++)
	{
		l=1;r=f[0]=0;
		for(int j=1;j<=b;j++)
		{
			while(l<=r && f[l]+n<=j)l++;
			while(l<=r && w[i][j]>w[i][f[r]])r--;
			f[++r]=j; q[i][j]=f[l];
		}
		for(int j=1;j<=b;j++)
		{
			while(ll[j]<=rr[j] && h[j][ll[j]]+n<=i)ll[j]++;
			while(ll[j]<=rr[j] && w[h[j][rr[j]]][q[h[j][rr[j]]][j]]<w[i][q[i][j]])rr[j]--;
			h[j][++rr[j]]=i;
			Max[i][j]=w[h[j][ll[j]]][q[h[j][ll[j]]][j]];
		}
	}
}
int main()
{
	read(a);read(b);read(n);
	for(int i=1;i<=a;i++)
		for(int j=1;j<=b;j++)read(w[i][j]);
	ans=inf;
	getMin();getMax();
	for(int i=n;i<=a;i++)
		for(int j=n;j<=b;j++)ans=min(ans,Max[i][j]-Min[i][j]);
	printf("%d\n",ans);
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值