P2216 [HAOI2007]理想的正方形
P2216 [HAOI2007]理想的正方形
有一个 \(a×b\) 的整数组成的矩阵,现请你从中找出一个 \(n×n\) 的正方形区域,使得该区域所有数中的最大值和最小值的差最小。
输入格式
第一行为三个整数,分别表示 \(a,b,n\) 的值;
第二行至第 \(a+1\) 行每行为 \(b\) 个非负整数,表示矩阵中相应位置上的数。
输出格式
输出仅一个整数,为 \(a×b\) 矩阵中所有“\(n×n\) 正方形区域中的最大整数和最小整数的差值”的最小值。
数据范围
\(2≤a,b≤1000\)
\(n≤a,n≤b,n≤100\)
矩阵中的所有数都不超过 \(10^9\)。
输入样例:
5 4 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2
输出样例:
1
题解
其实难点就是一维向二维变化的过程,如果是一维,我们可以用线段树,单调队列进行维护,
二维呢,其实就是多了\(n\)行,那么我们可以还是沿用一维的思路
先对\(n\)行分别进行处理,就相当于,把前k个数的最小值,全都摔到\(k\)这一列去,然后,再对算出来的每行 的
数据,进行合并处理,每\(n\)行合并处理一次。这样,就可以得出一个\(n*n\)的矩阵内的最大值和最小值了
\(row_{min}[i][j]\)表示第\(i\)行,从\(j-k+1\)列到\(j\)列这个区间的最小值
\(row_{max}[i][j]\)表示第\(i\)行,从\(j-k+1\)列到\(j\)列这个区间的最大值
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int w[N][N],row_min[N][N],row_max[N][N];
int q[N];
int n,m,k;
void solve(int a[],int len,int ans[],int typede)
{
int hh=0;
int tt=-1;
for(int i=1; i<=len; i++)
{
if(hh<=tt && q[hh]<i-k+1) hh++;
while(hh<=tt&&a[q[tt]]*typede<=a[i]*typede) tt--;
q[++tt]=i;
if(i>=k) ans[i]=a[q[hh]];
}
}
int main()
{
cin>>n>>m>>k;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
cin>>w[i][j];
for(int i=1; i<=n; i++)
{
solve(w[i],m,row_min[i],-1);
solve(w[i],m,row_max[i],1);
}
int temp[N];
int col_min[N];
int col_max[N];
int ans=0x3f3f3f3f;
for(int j=k; j<=m; j++)
{
for(int i=1;i<=n; i++) temp[i]=row_min[i][j];
solve(temp , n , col_min , -1);
for(int i=1; i<=n; i++) temp[i]=row_max[i][j];
solve(temp , n , col_max , 1);
for(int i=k; i<=n; i++)
{
ans=min(ans,col_max[i]-col_min[i]);
}
}
cout<<ans<<endl;
return 0;
}