这题看上去很麻烦,看到取矩阵就马上想得到用st表来完成,用nm log4(nm) 就可以了,其实是很快的。
很丑的代码<半年前打的>
#include
#include
#include
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
就是先把一个个矩阵的算出来,然后在不断的读。
但是有一种更好的方法。
就是单调队列维护矩阵极值。
原来你有一个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
#include
#include
#include
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][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]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);
}