原题链接:https://www.luogu.com.cn/problem/P2216
理想的正方形
题目描述
有一个a * b的整数组成的矩阵,现请你从中找出一个 n * n的正方形区域,使得该区域所有数中的最大值和最小值的差最小。
输入格式
第一行为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
题解
二维的滑动窗口,先对每一行做一次横向的滑动窗口,求出每一行长为 n n n的窗口滑过的最小/大值,再对求出的最小/大值做一次纵向的滑动窗口,这样求出的就是二维的 n × n n\times n n×n的窗口覆盖的最小/大值。
代码
看起来多,实际上都是复制粘贴的,一遍过针不戳。
#include<bits/stdc++.h>
using namespace std;
const int M=1e3+5;
int n,a,b,que[M],sqr[M][M],mn[M][M],mx[M][M],mn2[M][M],mx2[M][M],head,tail;
void hori(int x)
{
que[head=tail=1]=1;if(n==1)mn[x][1]=sqr[x][1];
for(int i=2;i<=b;++i)
{
for(;sqr[x][que[tail]]>=sqr[x][i]&&tail>=head;--tail);
que[++tail]=i;
for(;i-que[head]+1>n&&head<=tail;++head);
if(i>=n)mn[x][i]=sqr[x][que[head]];
}
que[head=tail=1]=1;if(n==1)mx[x][1]=sqr[x][1];
for(int i=2;i<=b;++i)
{
for(;sqr[x][que[tail]]<=sqr[x][i]&&tail>=head;--tail);
que[++tail]=i;
for(;i-que[head]+1>n&&head<=tail;++head);
if(i>=n)mx[x][i]=sqr[x][que[head]];
}
}
void verti(int x)
{
que[head=tail=1]=1;if(n==1)mn2[1][x]=mn[1][x];
for(int i=2;i<=a;++i)
{
for(;mn[que[tail]][x]>=mn[i][x]&&tail>=head;--tail);
que[++tail]=i;
for(;i-que[head]+1>n&&head<=tail;++head);
if(i>=n)mn2[i][x]=mn[que[head]][x];
}
que[head=tail=1]=1;if(n==1)mx2[1][x]=mx[1][x];
for(int i=2;i<=a;++i)
{
for(;mx[que[tail]][x]<=mx[i][x]&&tail>=head;--tail);
que[++tail]=i;
for(;i-que[head]+1>n&&head<=tail;++head);
if(i>=n)mx2[i][x]=mx[que[head]][x];
}
}
void in()
{
scanf("%d%d%d",&a,&b,&n);
for(int i=1;i<=a;++i)for(int j=1;j<=b;++j)scanf("%d",&sqr[i][j]);
}
void ac()
{
for(int i=1;i<=a;++i)hori(i);
for(int i=n;i<=b;++i)verti(i);
int ans=INT_MAX;
for(int i=n;i<=a;++i)for(int j=n;j<=b;++j)ans=min(ans,mx2[i][j]-mn2[i][j]);
printf("%d\n",ans);
}
int main()
{
in(),ac();
system("pause");
}