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;
}
原文地址:https://www.cnblogs.com/AmnesiacVisitor/p/7594790.html