题
简述:给定 n×mn×m的矩阵,求其所有k×kk×k的子矩阵中,极差最小的一个。(PS:极差=最大值-最小值)
其中 n,m≤1000n,m≤1000,k≤100k≤100。
解
一个静态二维区间极值问题。可以选择带 loglog 的数据结构维护,不过由于是静态,杀鸡就不用牛刀了,上单调队列要更快。(有点二维滑动窗口的感觉)
核心思路就是降维,我们不妨把一维压掉,只保留有用信息,比如我们将每一列(纵的叫列)存成一个数,表示该列的最大/小值(因为只有这俩有用啊)
我们先枚举行,从 11 到 ,
然后考虑每次枚举时的状态,此时是一个 k×mk×m 的矩阵,我们将每一列存下最大值和最小值,于是就得到两个数组 Maxi,MiniMaxi,Mini,长度均为 mm,接下来的问题就是对这两个一维的数组上求连续段极值,用单调队列维护。
时间复杂度 ,代码见下:
#include<bits/stdc++.h> //万能头文件并不支持所有OJ
using namespace std;
#define maxa 1002
#define R register
#define INF 2147483000
int a,b,n,ans=INF,G[maxa][maxa],min1[maxa],max1[maxa];
struct fifo
{
int l,r,q[maxa];
fifo(){l=r=0;memset(q,0,sizeof q);}
void cls() {l=r=0;}
void max_insert(int pos) {while(!empty()&&max1[q[r-1]]<=max1[pos])--r;q[r++]=pos;}
void min_insert(int pos) {while(!empty()&&min1[q[r-1]]>=min1[pos])--r;q[r++]=pos;}
void del(int pos) {while(!empty()&&front()<=pos)pop();}
void pop() {++l;}
int front() {return q[l];}
bool empty() {return l>=r;}
}maxq,minq;
int main()
{
scanf("%d %d %d",&a,&b,&n);
for (R int i=1;i<=a;++i)for (R int j=1;j<=b;++j)scanf("%d",&G[i][j]);
for (R int i=1;i+n-1<=a;++i)
{
maxq.cls();minq.cls();
for (R int j=1;j<=b;++j)
{
min1[j]=INF;max1[j]=-INF;
for (R int k=i;k<=i+n-1;++k) max1[j]=max(max1[j],G[k][j]),min1[j]=min(min1[j],G[k][j]);
maxq.del(j-n);maxq.max_insert(j);
minq.del(j-n);minq.min_insert(j);
(j >= n) ? ans=min(ans,max1[maxq.front()]-min1[minq.front()]) : 0;
}
}
printf("%d\n",ans);
return 0;
}