正题
这题看上去很麻烦,看到取矩阵就马上想得到用st表来完成,用nm log4(nm) 就可以了,其实是很快的。
很丑的代码<半年前打的>
#include<cstdio>
#include<cstdlib>
#include<cstring>
int a,b,n;
int max[1010][1010],min[1010][1010];
int logn=0;
int tot=1;
int ci[31];
int mmax(int x,int y) {return x>y?x:y;}
int mmin(int x,int y) {return x<y?x:y;}
int nowmax,nowmin;
int suan(int x,int y)
{
nowmax=0,nowmin=2e9;
nowmax=mmax(max[x][y],mmax(max[x+n-ci[logn]][y+n-ci[logn]],
mmax(max[x+n-ci[logn]][y],max[x][y+n-ci[logn]])));
nowmin=mmin(min[x][y],mmin(min[x+n-ci[logn]][y+n-ci[logn]],
mmin(min[x+n-ci[logn]][y],min[x][y+n-ci[logn]])));
return nowmax-nowmin;
}
int main()
{
scanf("%d %d %d",&a,&b,&n);
while(tot<=n)
{
tot*=2;
logn++;
}
logn--;
ci[0]=1;
for(int i=1;i<=logn;i++) ci[i]=(ci[i-1]<<1);
for(int i=1;i<=a;i++)
for(int j=1;j<=b;j++)
{
scanf("%d",&max[i][j]);
min[i][j]=max[i][j];
}
for(int k=1;k<=logn;k++)
for(int i=1;i+ci[k]-1<=a;i++)
for(int j=1;j+ci[k]-1<=b;j++)
{
max[i][j]=mmax(max[i][j],mmax(max[i+ci[k-1]][j+ci[k-1]],
mmax(max[i+ci[k-1]][j],max[i][j+ci[k-1]])));
min[i][j]=mmin(min[i][j],mmin(min[i+ci[k-1]][j+ci[k-1]],
mmin(min[i+ci[k-1]][j],min[i][j+ci[k-1]])));
}
int ans=2e9;
for(int i=1;i+n-1<=a;i++)
for(int j=1;j+n-1<=b;j++)
ans=mmin(ans,suan(i,j));
printf("%d",ans);
}
就是先把一个个矩阵的算出来,然后在不断的读。
但是有一种更好的方法。
就是单调队列维护矩阵极值。
原来你有一个n*m的矩阵,假如说你要从中取一个a*b的最大值,如果直接遍历要n*m*a*b
现在,我们先把每一行连续a个的最大值先处理出来。
如下图,描述样例,左边为原来的,右边的mmax[ i ][ j ]表示原数组第 i 行第 j 列 到 第 i 行第 j+n-1 列的最大值。
我们做好之后,就令ans[ i ][ j ]表示mmax数组中第 i 行第 j 列到第 i+n-1 行第 j 列的最大值。那么ans表示的就是第 i 行第 j 列到第 i+n-1 行到第 j+n-1 列的最大值。如下图。
求出来的就是这样的。
然后我们可以用一个单调队列来维护最大值,一个单调队列来维护最小值,最后用3nm的时间就可以完成操作。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
int n,a,b;
int s[1010][1010];
int mmax[1010][1010],mmin[1010][1010];
int totx[1010][1010],toty[1010][1010];
int list1[1010],list2[1010];
int st1=1,ed1=0,st2=1,ed2=0;
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",&s[i][j]);
for(int i=1;i<=a;i++){
st1=1,ed1=0,st2=1,ed2=0;
for(int j=1;j<=b;j++){
while(st1<=ed1 && s[i][j]<s[i][list1[ed1]]) ed1--;list1[++ed1]=j;
while(st2<=ed2 && s[i][j]>s[i][list2[ed2]]) ed2--;list2[++ed2]=j;
if(j>=n){
mmax[i][j-n+1]=s[i][list2[st2]];
mmin[i][j-n+1]=s[i][list1[st1]];
while(j-list1[st1]+1>=n) st1++;
while(j-list2[st2]+1>=n) st2++;
}
}
}
for(int j=1;j<=b-n+1;j++){
st1=1,ed1=0,st2=1,ed2=0;
for(int i=1;i<=a;i++){
while(st1<=ed1 && mmin[i][j]<mmin[list1[ed1]][j]) ed1--;list1[++ed1]=i;
while(st2<=ed2 && mmax[i][j]>mmax[list2[ed2]][j]) ed2--;list2[++ed2]=i;
if(i>=n){
totx[i-n+1][j]=mmax[list2[st2]][j];
toty[i-n+1][j]=mmin[list1[st1]][j];
while(i-list1[st1]+1>=n) st1++;
while(i-list2[st2]+1>=n) st2++;
}
}
}
int ans=1e9;
for(int i=1;i<=a-n+1;i++)
for(int j=1;j<=b-n+1;j++)
ans=min(ans,totx[i][j]-toty[i][j]);
printf("%d\n",ans);
}