P2216 [HAOI2007]理想的正方形-二维单调队列

  • 题意:有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域。
  • 使得该区域所有数中的最大值和最小值的差最小。
  • 思路:先用单调队列维护行,得到两个新的矩阵,每个点的含义是从此处往后k个的最大值(或最小值)。
  • 在对这两个新的矩阵进行列单调队列维护最值得到两个新的矩阵此时每个点的含义就是从此处开始的大小n*n的区域内的
  • 最大值(最小值)。最后暴力枚举一下求一个最值差最小即可。
  • P2216 [HAOI2007]理想的正方形-二维单调队列_第1张图片
  • #include
    using namespace std;
    #define inf 0x3f3f3f3f
    #define maxn 1234
    int a[maxn][maxn],n,m;
    int miii[maxn][maxn],k;
    int ans1[maxn][maxn];
    int qmin[maxn],l,r;
    int qmax[maxn],s,t,ans;
    int maaa[maxn][maxn];
    int ans2[maxn][maxn];
    void cal()
    {
        for(int i=1; i<=n; i++)
        {
            t=r=-1;
            s=l=0;
            for(int j=m; j>0; j--)
            {
                while(l<=r&&a[i][j]k)l++;
                while(s<=t&&a[i][j]>a[i][qmax[t]])t--;
                qmax[++t]=j;
                while(qmax[s]-qmax[t]+1>k)s++;
                if(m-j+1>=k)
                {
                    miii[i][j]=a[i][qmin[l]];
                    maaa[i][j]=a[i][qmax[s]];
                }
            }
        }
    }
    void ok()
    {
        for(int j=1; j<=m-k+1; j++)
        {
            t=r=-1;
            s=l=0;
            for(int i=n; i>0; i--)
            {
                while(l<=r&&miii[i][j]k)l++;
                while(s<=t&&maaa[i][j]>maaa[qmax[t]][j])t--;
                qmax[++t]=i;
                while(qmax[s]-qmax[t]+1>k)s++;
                if(n-i+1>=k)
                {
                    ans1[i][j]=miii[qmin[l]][j];
                    ans2[i][j]=maaa[qmax[s]][j];
                }
            }
        }
    }
    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]);
        cal();
        ok();
        ans=inf;
        for(int i=1; i<=n-k+1; i++)
            for(int j=1; j<=m-k+1; j++)
                ans=min(ans,ans2[i][j]-ans1[i][j]);
        printf("%d\n",ans);
        return 0;
    }
    

     

 

你可能感兴趣的:(单调栈-队列-ST)