标签: 单调队列
题目描述
有一个ab的整数组成的矩阵,现请你从中找出一个nn的正方形区域,使得该区域所有数中的最大值和最小值的差最小。
输入格式:
第一行为3个整数,分别表示a,b,n的值
第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数。每行相邻两数之间用一空格分隔。
输出格式:
仅一个整数,为ab矩阵中所有“nn正方形区域中的最大整数和最小整数的差值”的最小值。
输入样例#1:
5 4 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2
输出样例#1:
1
问题规模
(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<=100
分析
先枚举正方形所在的行,确定行之后,用滑动窗口分别求出窗口中的最大和最小值.
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int maxn=1050;
const int inf=0x3f3f3f3f;
int s[maxn][maxn],a,b,n;
struct Queue
{
int q[maxn],t,h;
Queue(){
t=0,h=1;
}
void push(int x,int y,int f=1){
while(t-h+1>=1&&s[q[t]][y]*f>=s[x][y]*f) t--;
q[++t]=x;
}
void pop(int x){
while(t-h+1>=1&&q[h]<x) h++;
}
int top(){
return q[h];
}
}m[maxn],M[maxn];
int main(int argc, char const *argv[])
{
scanf("%d%d%d", &a,&b,&n);
for (int i = 0; i < a; ++i)
{
for (int j = 0; j < b; ++j)
{
scanf("%d", &s[i][j]);
}
}
int ans=inf;
for (int i = 0; i+n <= a; ++i)
{
if(i==0)
for (int j = 0; j < b; ++j)
{
for (int k = 0; k < n; ++k)
{
m[j].push(k,j);
M[j].push(k,j,-1);
}
}
else
for (int j = 0; j < b; ++j)
{
m[j].pop(i),m[j].push(i+n-1,j);
M[j].pop(i),M[j].push(i+n-1,j,-1);
}
int h=1,t=0,X[maxn],Y[maxn];
int hh=1,tt=0,XX[maxn],YY[maxn];
for (int j = 0; j+n <= b; ++j)
{
if(j==0){
for (int k = j; k < j+n; ++k)
{
int x=m[k].top();
while(t-h+1>=1&&s[X[t]][Y[t]]>=s[x][k]) t--;
X[++t]=x,Y[t]=k;
x=M[k].top();
while(tt-hh+1>=1&&s[XX[tt]][YY[tt]]<=s[x][k]) tt--;
XX[++tt]=x,YY[tt]=k;
}
}
else{
int x=m[j+n-1].top();
while(t-h+1>=1&&s[X[t]][Y[t]]>=s[x][j+n-1]) t--;
X[++t]=x,Y[t]=j+n-1;
x=M[j+n-1].top();
while(tt-hh+1>=1&&s[XX[tt]][YY[tt]]<=s[x][j+n-1]) tt--;
XX[++tt]=x,YY[tt]=j+n-1;
}
while(Y[h]<j) h++;
while(YY[hh]<j) hh++;
ans=min(ans,s[XX[hh]][YY[hh]]-s[X[h]][Y[h]]);
}
}
printf("%d
", ans);
return 0;
}