[HAOI2007]理想的正方形

传送门

DP
其实主要是单调队列..
数据最大为一千,考虑O(n^2)的做法
在a*b的矩形中找出n^n的正方形再怎么样至少也要把a和b分别枚举吧。
必须考虑O(1)处理单个正方形最大最小值。
容易发现正方形大小不变为n,如果只看一行,就是滑动窗口,那么处理单个正方形最大最小值时间复杂度为O(n);
仔细思考后(没错不管是什么题目仔细思考后就一定能想出正解..)发现如果把单调队列更新的值存起来为mxa[i][j],mxa[i][j]表示第i行以第j列为结尾的数列的最大值.
则要求mxb[i][j](mxb[i][j]表示以i,j为右下角的正方形的最大值)就只要对mxa竖着跑b遍单调队列就好了..
正解出来了,但要注意i,j,的范围..

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
int n,m,k;
int a[1007][1007];
int posx[1007],posi[1007];
int mxa[1007][1007],mia[1007][1007];
int mxb[1007][1007],mib[1007][1007];
int main()
{
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++)
    {
        int heax=1,lasx=0,heai=1,lasi=0;
        for(int j=1;j<=m;j++)
        {
            scanf("%d",&a[i][j]);
            while(heax<=lasx&&a[i][j]>=a[i][posx[lasx]]) lasx--;
            while(heai<=lasi&&a[i][j]<=a[i][posi[lasi]]) lasi--;
            while(heax<=lasx&&j-posx[heax]>=k) heax++;
            while(heai<=lasi&&j-posi[heai]>=k) heai++;
            posx[++lasx]=j; posi[++lasi]=j;
            if(j>=k) mxa[i][j-k+1]=a[i][posx[heax]],mia[i][j-k+1]=a[i][posi[heai]];
        }
    }
    memset(posx,0,sizeof(posx));
    memset(posi,0,sizeof(posi));
    for(int j=1;j<=m-k+1;j++)
    {
        int heax=1,lasx=0,heai=1,lasi=0;
        for(int i=1;i<=n;i++)
        {
            while(heax<=lasx&&mxa[i][j]>=mxa[posx[lasx]][j]) lasx--;
            while(heai<=lasi&&mia[i][j]<=mia[posi[lasi]][j]) lasi--;
            while(heax<=lasx&&i-posx[heax]>=k) heax++;
            while(heai<=lasi&&i-posi[heai]>=k) heai++;
            posx[++lasx]=i; posi[++lasi]=i;
            if(i>=k) mxb[i-k+1][j]=mxa[posx[heax]][j],mib[i-k+1][j]=mia[posi[heai]][j];
        }
    }
    int ans=2147483647;
    for(int i=1;i<=n-k+1;i++)
        for(int j=1;j<=m-k+1;j++)
            ans=min(ans,mxb[i][j]-mib[i][j]);
    cout<<ans;
    return 0;
}

 

posted @ 2018-08-20 17:04  LLTYYC  阅读(183)  评论(0编辑  收藏  举报