一本通1604理想的正方形

Posted gaojunonly1

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一本通1604理想的正方形相关的知识,希望对你有一定的参考价值。

1604:理想的正方形

时间限制: 1000 ms         内存限制: 524288 KB

【题目描述】

原题来自:HAOI 2007

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

【输入】

第一行为三个整数,分别表示 a,b,n 的值;

第二行至第 a+1 行每行为 b 个非负整数,表示矩阵中相应位置上的数。

【输出】

输出仅一个整数,为 a×b 矩阵中所有「n×n 正方形区域中的最大整数和最小整数的差值」的最小值。

【输入样例】

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

【输出样例】

1

【提示】

数据范围与提示:

对于 20% 的数据 2a,b100,n10

对于 100% 的数据 2a,b1000,na,nb,n100,矩阵中的所有数都不超过 109

 

sol:这题有两种做法(二维ST表,二维单调队列)

二维ST表:顾名思义二维的ST表,ST[i][j][k]表示以(i,j)为左下角,边长为 2的正方形的最值,很好处理

技术图片
#include <bits/stdc++.h>
using namespace std;
typedef int ll;
inline ll read()
{
    ll s=0;
    bool f=0;
    char ch= ;
    while(!isdigit(ch))
    {
        f|=(ch==-);
        ch=getchar();
    }
    while(isdigit(ch))
    {
        s=(s<<3)+(s<<1)+(ch^48);
        ch=getchar();
    }
    return (f)?(-s):(s);
}
#define R(x) x=read()
inline void write(ll x)
{
    if(x<0)
    {
        putchar(-);
        x=-x;
    }
    if(x<10)
    {
        putchar(x+0);
        return;
    }
    write(x/10);
    putchar((x%10)+0);
    return;
}
inline void writeln(ll x)
{
    write(x);
    putchar(
);
    return;
}
#define W(x) write(x),putchar(‘ ‘)
#define Wl(x) writeln(x)
const int N=1005,B=10,inf=0x3f3f3f3f;
int n,m,K,St_Max[N][N][B],St_Min[N][N][B];
inline int Max4(int a,int b,int c,int d)
{
    int e=max(a,b),f=max(c,d); return max(e,f);
}
inline int Min4(int a,int b,int c,int d)
{
    int e=min(a,b),f=min(c,d); return min(e,f);
}
int main()
{
//    freopen("square1.in","r",stdin);
    int i,j,k;
    R(n); R(m); R(K);
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=m;j++) St_Max[i][j][0]=St_Min[i][j][0]=read();
    }
    for(i=1;i<=7;i++)
    {
        for(j=1;j+(1<<i)-1<=n;j++)
        {
            for(k=1;k+(1<<i)-1<=m;k++)
            {
                St_Max[j][k][i]=Max4(St_Max[j][k][i-1],St_Max[j+(1<<(i-1))][k][i-1],St_Max[j][k+(1<<(i-1))][i-1],St_Max[j+(1<<(i-1))][k+(1<<(i-1))][i-1]);
                St_Min[j][k][i]=Min4(St_Min[j][k][i-1],St_Min[j+(1<<(i-1))][k][i-1],St_Min[j][k+(1<<(i-1))][i-1],St_Min[j+(1<<(i-1))][k+(1<<(i-1))][i-1]);
            }
        }
    }
    int ans=inf;
    int oo;
    for(oo=0;;oo++) if(((1<<oo)<=K)&&((1<<(oo+1))>=K)) break;
    for(i=1;i+K-1<=n&&ans;i++)
    {
        for(j=1;j+K-1<=m&&ans;j++)
        {
            int o1=Max4(St_Max[i][j][oo],St_Max[i+K-1-(1<<oo)+1][j][oo],St_Max[i][j+K-1-(1<<oo)+1][oo],St_Max[i+K-1-(1<<oo)+1][j+K-1-(1<<oo)+1][oo]);
            int o2=Min4(St_Min[i][j][oo],St_Min[i+K-1-(1<<oo)+1][j][oo],St_Min[i][j+K-1-(1<<oo)+1][oo],St_Min[i+K-1-(1<<oo)+1][j+K-1-(1<<oo)+1][oo]);
            ans=min(ans,o1-o2);
        }
    }
    Wl(ans);
    return 0;
}
/*
input
5 4 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2
output
1
*/
二维ST表

二维单调队列:先处理出Min[i][j][0]表示以(i,j)为右下角的长度为n的一整条中的最小值,用单调队列a*b就可以处理出来了

再用Min[i][j][0]到列上去跑单调队列,得到Min[i][j][1]表示以(i,j)为右下角的一个n*n的正方形中的最小值

技术图片
#include <bits/stdc++.h>
using namespace std;
typedef int ll;
inline ll read()
{
    ll s=0;
    bool f=0;
    char ch= ;
    while(!isdigit(ch))
    {
        f|=(ch==-);
        ch=getchar();
    }
    while(isdigit(ch))
    {
        s=(s<<3)+(s<<1)+(ch^48);
        ch=getchar();
    }
    return (f)?(-s):(s);
}
#define R(x) x=read()
inline void write(ll x)
{
    if(x<0)
    {
        putchar(-);
        x=-x;
    }
    if(x<10)
    {
        putchar(x+0);
        return;
    }
    write(x/10);
    putchar((x%10)+0);
    return;
}
inline void writeln(ll x)
{
    write(x);
    putchar(
);
    return;
}
#define W(x) write(x),putchar(‘ ‘)
#define Wl(x) writeln(x)
const int N=1005,inf=0x3f3f3f3f;
int n,m,L,Val[N][N];
int Min[N][N][2],Max[N][N][2];
struct Record
{
    int Shuz,Weiz;
}Ddq[N];
int main()
{
//    freopen("square1.in","r",stdin);
    int i,j,Head,Tail;
    R(n); R(m); R(L);
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=m;j++) R(Val[i][j]);
    }
    for(i=1;i<=n;i++)
    {
        Head=1; Tail=0;
        for(j=1;j<=m;j++)
        {
            while(Head<=Tail&&Val[i][j]<Ddq[Tail].Shuz) Tail--;
            Ddq[++Tail]=(Record){Val[i][j],j};
            while(Head<Tail&&Ddq[Head].Weiz<j-L+1) Head++;
            Min[i][j][0]=Ddq[Head].Shuz;
        }
        Head=1; Tail=0;
        for(j=1;j<=m;j++)
        {
            while(Head<=Tail&&Val[i][j]>Ddq[Tail].Shuz) Tail--;
            Ddq[++Tail]=(Record){Val[i][j],j};
            while(Head<Tail&&Ddq[Head].Weiz<j-L+1) Head++;
            Max[i][j][0]=Ddq[Head].Shuz;
        }
    }
    for(j=L;j<=m;j++)
    {
        Head=1; Tail=0;
        for(i=1;i<=n;i++)
        {
            while(Head<=Tail&&Min[i][j][0]<Ddq[Tail].Shuz) Tail--;
            Ddq[++Tail]=(Record){Min[i][j][0],i};
            while(Head<Tail&&Ddq[Head].Weiz<i-L+1) Head++;
            Min[i][j][1]=Ddq[Head].Shuz;
        }
        Head=1; Tail=0;
        for(i=1;i<=n;i++)
        {
            while(Head<=Tail&&Max[i][j][0]>Ddq[Tail].Shuz) Tail--;
            Ddq[++Tail]=(Record){Max[i][j][0],i};
            while(Head<Tail&&Ddq[Head].Weiz<i-L+1) Head++;
            Max[i][j][1]=Ddq[Head].Shuz;
        }
    }
    int ans=inf;
    for(i=L;i<=n;i++)
    {
        for(j=L;j<=m;j++) ans=min(ans,Max[i][j][1]-Min[i][j][1]);
    }
    Wl(ans);
    return 0;
}
/*
input
5 4 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2
output
1
*/
二维单调队列

以上是关于一本通1604理想的正方形的主要内容,如果未能解决你的问题,请参考以下文章

#10172. 「一本通 5.4 练习 1」涂抹果酱 题解

一本通网站基础篇完结纪念

一本通1154:亲和数

BZOJ1047: [HAOI2007]理想的正方形

[luoguP2216] [HAOI2007]理想的正方形(二维单调队列)

理想的正方形 单调队列优化DP