[bzoj1047][HAOI2007]理想的正方形_动态规划_单调队列

理想的正方形 bzoj-1047 HAOI-2007

题目大意有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小。

注释:$2\le a,b,n\le 10^3$,$n\le min(a,b)$。

想法:我的思路简直要死。通常的,我们优化暴力来完成对题目的求解。首先,想暴力。漂亮的暴力没想到,倒是想到一个a*b*n*n的,就是枚举每一个点,如果这个点可以作为n*n正方形的左上角,我就暴力枚举这个正方形的所有点。时间复杂度:O((a-n)*(b-n)*n*n)O(挖坑代填过不去)。想怎么优化:首先我们想,暴力的时候是连续的将n*n个数取最大值,我们可以怎么优化?我们可以对于每一行来讲维护一个窗口长度为n的单调队列来求出以每一个点结尾的前n个数的最值。然后我.. ....tm自以为是正解然后开始敲。自然T了之后还不知道怎么肥四,以为bz评测机又jb炸了。仔细分析了一下发现不太对。总的时间复杂度是a*b*n的,满的话是$10^9$的,不T才怪... ...想想再优化优化就好了嘛。对于求出来的矩阵,maxn[i][j]表示原矩阵以(i,j)为起点前n个数的最值。我们可以对maxn矩阵再维护一个窗口长度为n的单调队列,这样的话查询就是O(1)的了qwq.

最后,附上丑陋的代码... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define inf 2000000000
#define ll long long 
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int a,b,n;
int v[1005][1005],mx[1005][1005],mn[1005][1005],t1[1005],t2[1005];
int val[1005],pos[1005];
void pre()
{
    int l,r;
    for(int i=1;i<=a;i++)
    {
        l=1,r=1;
        for(int j=1;j<=b;j++)
        {
            while(l<r&&val[r-1]<=v[i][j])r--;
            val[r]=v[i][j];pos[r]=j;r++;
            if(pos[l]==j-n)l++;
            if(j>=n)mx[i][j]=val[l];
        }
        l=1,r=1;
        for(int j=1;j<=b;j++)
        {
            while(l<r&&val[r-1]>=v[i][j])r--;
            val[r]=v[i][j];pos[r]=j;r++;
            if(pos[l]==j-n)l++;
            if(j>=n)mn[i][j]=val[l];
        }
    }
}
void solve()
{
    int ans=inf;
    int l,r;
    for(int i=n;i<=b;i++)
    {
        l=1,r=1;
        for(int j=1;j<=a;j++)
        {
            while(l<r&&val[r-1]>=mn[j][i])r--;
            val[r]=mn[j][i];pos[r]=j;r++;
            if(pos[l]==j-n)l++;
            if(j>=n)t1[j]=val[l];
        }
        l=1,r=1;
        for(int j=1;j<=a;j++)
        {
            while(l<r&&val[r-1]<=mx[j][i])r--;
            val[r]=mx[j][i];pos[r]=j;r++;
            if(pos[l]==j-n)l++;
            if(j>=n)t2[j]=val[l];
        }
        for(int i=n;i<=a;i++)ans=min(ans,t2[i]-t1[i]);
    }
    printf("%d\n",ans);
}
int main()
{
    a=read();b=read();n=read();
    for(int i=1;i<=a;i++)
        for(int j=1;j<=b;j++)
            v[i][j]=read();
    pre();
    solve();
    return 0;
}

 

 小结:单调队列是优化dp的一种比较优秀的手段。而且好写好调,比什么单调栈强多了... ...

posted @ 2018-05-13 16:39  JZYshuraK_彧  阅读(218)  评论(0编辑  收藏  举报