P2216 [HAOI2007]理想的正方形

题目描述

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

输入格式

第一行为 \(3\) 个整数,分别表示 \(a,b\) 的值。

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

输出格式

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

输入输出样例

输入 #1

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

输出 #1

1

Solution

这个题很裸,只要能 \(O(abn)\) 及以内地查询二维区间最值的算法都可以过。

数据也不卡你,然后实际上简单优化一下的暴力就能过。

然后像什么ST表啊、RMQ、单调队列啊甚至线段树也都可以过。

思路很简单,先一遍求区间最值算法求出每列(或行)每个长度为 \(n\) 的区间的最大·小值,然后对每行(或列)用之前算出的最大·小值再做一次,求出每个 长度为 \(n\) 的区间内的 \(n\) 维列(或行)向量中数的最大值的最大值和最小值的最小值相减,然后取最小值即可。

我写的单调队列做法, \(O(ab)\) 复杂度。

但是,但是,STL 的 deque 的常数怎么可以这么大???吸个氧速度快了6倍还多QAQ

Code

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<deque>
#define N 1010
#define min(a,b) (a) < (b) ? (a) : (b)
#define max(a,b) (a) > (b) ? (a) : (b)
#define LL long long
using namespace std;
inline int read()
{
    int x = 0, f = 1; char c = getchar();
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') {x = (x << 1) + (x << 3) + (c ^ 48); c=getchar();}
    return x * f;
}
struct number{
    LL num, index;
};
int main()
{
    int a = read(), b = read(), n = read();
    LL mp[N][N], cmax[N][N], cmin[N][N];
    LL ans = 1e10 + 1;
    deque<number> cmaxq, cminq;
    for(int i=1;i<=a;i++)
        for(int j=1;j<=b;j++)
            mp[i][j] = read();
    
    for(int c=1;c<=b;c++){                                              //预处理每列区间最大、小值
        cmaxq.clear(), cminq.clear();
        for(int r=1;r<=a;r++) {
            struct number nw = {mp[r][c], r};
            while(cminq.size() && cminq.back().num >= mp[r][c])
                cminq.pop_back();
            cminq.push_back(nw);
            while(r - cminq.front().index + 1 > n) cminq.pop_front();
            if(r >= n) cmin[r - n + 1][c] = cminq.front().num;

            while(cmaxq.size() && cmaxq.back().num <= mp[r][c])
                cmaxq.pop_back();
            cmaxq.push_back(nw);
            while(r - cmaxq.front().index + 1 > n) cmaxq.pop_front();
            if(r >= n) cmax[r - n + 1][c] = cmaxq.front().num;
        }
    }

    for(int r=1;r<=a-n+1;r++) { 
        cmaxq.clear(), cminq.clear();
        for(int c=1;c<=b;c++) {                                         //处理所有以(r, c)为左上角的矩形
            struct number nwmin = {cmin[r][c], c};
            struct number nwmax = {cmax[r][c], c};
            LL tmmax, tmmin;
            while(cminq.size() && cminq.back().num >= cmin[r][c])
                cminq.pop_back();
            cminq.push_back(nwmin);
            while(c - cminq.front().index + 1 > n) cminq.pop_front();
            if(c >= n) tmmin = cminq.front().num;

            while(cmaxq.size() && cmaxq.back().num <= cmax[r][c])
                cmaxq.pop_back();
            cmaxq.push_back(nwmax);
            while(c - cmaxq.front().index + 1 > n) cmaxq.pop_front();
            if(c >= n) tmmax = cmaxq.front().num;

            if(c >= n) ans = min(ans, tmmax - tmmin);
        }
    }
    
    printf("%lld\n", ans);
    return 0;
}   
上一篇:数据结构与算法(一)--- 栈


下一篇:iOS开发_开启关闭侧滑手势