【BZOJ 1047】 [HAOI2007]理想的正方形

1047: [HAOI2007]理想的正方形

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 1685   Solved: 891
[ 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


单调队列。


其实这道题比较简单,就是两次队列优化的dp。


g[i][j]表示在第j列,i-n+1到i的最小值。O(ab)可求出。


f[i][j]表示以(i,j)为右下角n*n的矩阵中的最小值,用g[i][j]来求,就转变成一维的了。O(ab)可求出。


最大最小就是分别按照正负来做即可。


#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#define inf 0x3f3f3f3f
using namespace std;
int a,b,n,g[1005][1005],f[1005][1005][2],x[1005][1005];
struct qu
{
	int v,p;
}q[1005];
void Getgf(int k)
{
	for (int j=1;j<=b;j++)
	{
		int l=1,r=0;
		for (int i=1;i<=a;i++)
		{
			while (r&&r>=l&&x[i][j]<=q[r].v)
	            r--;
			while (l&&l<=r&&q[l].p<=i-n)
				l++;
			q[++r].v=x[i][j],q[r].p=i;
			g[i][j]=q[l].v;
		}
	}
	for (int i=n;i<=a;i++)
	{
		int l=1,r=0;
		for (int j=1;j<=b;j++)
		{
			while (r&&r>=l&&g[i][j]<=q[r].v)
	            r--;
			while (l&&l<=r&&q[l].p<=j-n)
				l++;
			q[++r].v=g[i][j],q[r].p=j;
			f[i][j][k]=q[l].v;
		}
	}
}
int main()
{
        scanf("%d%d%d",&a,&b,&n);
	for (int i=1;i<=a;i++)
		for (int j=1;j<=b;j++)
			scanf("%d",&x[i][j]);
	Getgf(0);
	for (int i=1;i<=a;i++)
		for (int j=1;j<=b;j++)
			x[i][j]=-x[i][j];
	Getgf(1);
	int ans=inf;
	for (int i=n;i<=a;i++)
		for (int j=n;j<=b;j++)
			ans=min(ans,-f[i][j][1]-f[i][j][0]);
	cout<<ans<<endl;
	return 0;
}




感悟:

1.这道题本质就是把二维的问题转化成一维的来做

你可能感兴趣的:(dp,OI,bzoj,单调队列优化)