bzoj 1047
单调队列维护单调递增或递减。
以求最大值为例,先可以扫描每一行,求出每个数向前n个数的最大值,第i行第j列向前n个数的最大值记为max[i][j]。
如果有一行中后面的数比前面大,那么前面比它小的数已经没有用了,就把它从队列中弹出。
然后再从第n列开始向后扫描每一列,就可以求出每个n*n矩阵的最大值。
如果有一列中下面的max比上面的大,那么上面比它小的也已经没有用了,把它从队列中弹出。
最小值也是类似。代码虽然不是很短,但是逻辑还是很清晰的。
#include<cstdio> #include<cctype> #include<algorithm> using namespace std; int read(){ char c; while(!isdigit(c=getchar())); int x=c-'0'; while(isdigit(c=getchar())) x=x*10+c-'0'; return x; } int v[1001][1001],q1[1001][2],q2[1001][2],ans1[1001][1001],ans2[1001][1001]; int main(){ int a=read(),b=read(),n=read(),ans=2e9; for(int i=1;i<=a;i+=1) for(int j=1;j<=b;j+=1) v[i][j]=read(); for(int i=1;i<=a;i+=1){ int h1=0,t1=0,h2=0,t2=0; for(int j=1;j<=b;j+=1){ while(h1<t1 && q1[h1][1]<=j-n) h1++; while(h2<t2 && q2[h2][1]<=j-n) h2++; while(h1<t1 && v[i][j]>=q1[t1-1][0]) t1--; while(h2<t2 && v[i][j]<=q2[t2-1][0]) t2--; q1[t1][0]=v[i][j]; q1[t1++][1]=j; ans1[j][i]=q1[h1][0]; q2[t2][0]=v[i][j]; q2[t2++][1]=j; ans2[j][i]=q2[h2][0]; } } for(int i=n;i<=b;i+=1){ int h1=0,t1=0,h2=0,t2=0; for(int j=1;j<=a;j+=1){ while(h1<t1 && q1[h1][1]<=j-n) h1++; while(h2<t2 && q2[h2][1]<=j-n) h2++; while(h1<t1 && ans1[i][j]>=q1[t1-1][0]) t1--; while(h2<t2 && ans2[i][j]<=q2[t2-1][0]) t2--; q1[t1][0]=ans1[i][j]; q1[t1++][1]=j; q2[t2][0]=ans2[i][j]; q2[t2++][1]=j; if(j>=n) ans=min(q1[h1][0]-q2[h2][0],ans); } } printf("%d",ans); return 0; }