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

  • 题意:有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域。
  • 使得该区域所有数中的最大值和最小值的差最小。
  • 思路:先用单调队列维护行,得到两个新的矩阵,每个点的含义是从此处往后k个的最大值(或最小值)。
  • 在对这两个新的矩阵进行列单调队列维护最值得到两个新的矩阵此时每个点的含义就是从此处开始的大小n*n的区域内的
  • 最大值(最小值)。最后暴力枚举一下求一个最值差最小即可。
  • #include<bits/stdc++.h>
    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]<a[i][qmin[r]])r--;
                qmin[++r]=j;
                while(qmin[l]-qmin[r]+1>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]<miii[qmin[r]][j])r--;
                qmin[++r]=i;
                while(qmin[l]-qmin[r]+1>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;
    }
    

     

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值