zoukankan      html  css  js  c++  java
  • [BZOJ]1047 理想的正方形(HAOI2007)

      真·水题。小C本来是不想贴出来的,但是有一股来自东方的神秘力量催促小C发出来。

    Description

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

    Input

      第一行为3个整数,分别表示a,b,n的值第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数。每行相邻两数之间用一空格分隔。

    Output

      仅一个整数,为a*b矩阵中所有“n*n正方形区域中的最大整数和最小整数的差值”的最小值。

    Sample Input

      5 4 2
      1 2 5 6
      0 17 16 0
      16 17 2 1
      2 10 2 1
      1 2 2 2

    Sample Output

      1

    HINT

      2<=a,b<=1000,n<=a,n<=b,n<=1000

    Solution

      如果你是按照BZOJ第一页AC人数做下来的话,你的思路会被前一题稍微套路一下。

      回归正题,拿到这题我们正常的思路就是枚举所有矩阵,计算最大最小值更新答案。

      暴力O(n^4),二维线段树O(n^2logn)……发现可以降维(先做第一维,再做第二维)……发现询问区间长度固定……

      单调队列啊……

      每一行都维护两个单调队列(最大最小值),a行同时进行维护。

      维护到所有可能的右端点时,把维护的这a个最大/小值拿出来,在列上做一遍单调队列,顺便更新答案。

      时间复杂度O(n^2)。

      题解写得比较意识流,但小C认为如果你没懂不是小C的错。

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #define MN 1005
    #define INF 0x3FFFFFFF
    using namespace std;
    struct que
    {
        int hd,tl,q1[MN],q2[MN];
        void clear() {hd=1; tl=0;}
        int top() {return q2[hd];}
        void push(int x,int y,int g)
        {
            for (;hd<=tl&&((y>q2[tl])^g);--tl);
            ++tl; q1[tl]=x; q2[tl]=y;
        }
        void pop(int x) {for (;hd<=tl&&q1[hd]<=x;++hd);}
    }sdu[MN],sdd[MN],su,sd;
    int a[MN][MN];
    int n,m,p,ans;
    
    inline int read()
    {
        int n=0,f=1; char c=getchar();
        while (c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
        while (c>='0' && c<='9') {n=n*10+c-'0'; c=getchar();}
        return n*f;
    }
    
    int main()
    {
        register int i,j;
        n=read(); m=read(); p=read(); ans=INF;
        for (i=1;i<=n;++i)
            for (j=1;j<=m;++j) a[i][j]=read();
        for (i=1;i<=n;++i) sdu[i].clear(),sdd[i].clear();
        for (i=1;i<=n;++i)
            for (j=1;j<p;++j) sdu[i].push(j,a[i][j],0),sdd[i].push(j,a[i][j],1);
        for (i=p;i<=m;++i)
        {
            su.clear(); sd.clear();
            for (j=1;j<=n;++j)
            {
                sdu[j].push(i,a[j][i],0); sdu[j].pop(i-p);
                sdd[j].push(i,a[j][i],1); sdd[j].pop(i-p);
                su.push(j,sdu[j].top(),0); su.pop(j-p);
                sd.push(j,sdd[j].top(),1); sd.pop(j-p);
                if (j>=p) ans=min(ans,su.top()-sd.top());
            }
        }
        printf("%d",ans);
    }

    Last Word

      小C才不会告诉你把这题贴出来的原因是小C觉得自己的代码好看。

  • 相关阅读:
    准备 FRM 考试——方法、工具与教训
    930. 和相同的二元子数组 前缀和
    1906. 查询差绝对值的最小值 前缀和
    剑指 Offer 37. 序列化二叉树 二叉树 字符串
    815. 公交路线 BFS
    518. 零钱兑换 II dp 完全背包
    1049. 最后一块石头的重量 II dp
    5779. 装包裹的最小浪费空间 二分
    5778. 使二进制字符串字符交替的最少反转次数 字符串 滑动窗口
    474. 一和零 dp
  • 原文地址:https://www.cnblogs.com/ACMLCZH/p/7425605.html
Copyright © 2011-2022 走看看