[BZOJ1047][HAOI2007]理想的正方形(单调队列)

题目描述

传送门

题解

由题目可知只有每一部分的最大值和最小值是有价值的。
单调队列只能处理一维的情况,二维的不好处理。那么我们可以考虑一维一维处理,比如说,先做列再做行。
可以将每一列每一个长度为n的区间用单调队列处理出来最大值和最小值,然后缩成一个点。
然后将每一行缩成的那些点每一个长度为n的区间再用单调队列处理出来最大值和最小值,这样就相当于求出了n*n的矩形的最大值和最小值统计答案即可。

代码

#include
#include
#include
using namespace std;
#define N 2005
#define inf 1000000001

int n,m,k,lmax,rmax,lmin,rmin,ans=inf;
int a[N][N];
struct hp{int Max,Min;}b[N][N];
struct hq{int id,val;}qmax[N],qmin[N];

void push(int i,int val)
{
    while (lmaxwhile (lminval) --rmin;
    qmin[++rmin].id=i,qmin[rmin].val=val;
}
hp pop(int i)
{
    while (lmax1].idint Max=qmax[lmax+1].val;
    while (lmin1].idint Min=qmin[lmin+1].val;
    return (hp){Max,Min};
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for (int i=1;i<=n;++i)
        for (int j=1;j<=m;++j)
            scanf("%d",&a[i][j]);
    for (int i=1;i<=m;++i)
    {
        lmax=rmax=lmin=rmin=0;
        for (int j=1;j<=k;++j) push(j,a[j][i]);
        b[1][i]=pop(1);
        for (int j=k+1;j<=n;++j)
        {
            push(j,a[j][i]);
            b[j-k+1][i]=pop(j-k+1);
        }
    }
    for (int i=1;i<=n-k+1;++i)
    {
        lmax=rmax=lmin=rmin=0;
        for (int j=1;j<=k;++j) push(j,b[i][j].Max),push(j,b[i][j].Min);
        hp now=pop(1);ans=min(ans,now.Max-now.Min);
        for (int j=k+1;j<=m;++j)
        {
            push(j,b[i][j].Max);push(j,b[i][j].Min);
            now=pop(j-k+1);ans=min(ans,now.Max-now.Min);
        }
    }
    printf("%d\n",ans);
}


你可能感兴趣的:(题解,省选,单调队列)