一本通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% 的数据 2≤a,b≤100,n≤10;

对于 100% 的数据 2≤a,b≤1000,n≤a,n≤b,n≤100,矩阵中的所有数都不超过 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(‘\n‘);
    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(‘\n‘);
    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
*/

二维单调队列

原文地址:https://www.cnblogs.com/gaojunonly1/p/10380889.html

时间: 2024-10-30 06:33:40

一本通1604理想的正方形的相关文章

[BZOJ1047][HAOI2007]理想的正方形

1047: [HAOI2007]理想的正方形 Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 3481  Solved: 1917 [Submit][Status][Discuss] Description 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值 的差最小. Input 第一行为3个整数,分别表示a,b,n的值第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数.每 行相邻

【BZOJ 1047】 [HAOI2007]理想的正方形

1047: [HAOI2007]理想的正方形 Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 1685  Solved: 891 [Submit][Status] Description 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小. Input 第一行为3个整数,分别表示a,b,n的值第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数.每行相邻两数之间用一空格分隔.

RAM——[HAOI2007]理想的正方形

题目:[HAOI2007]理想的正方形 描述: [问题描述] 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小. [输入]: 第一行为3个整数,分别表示a,b,n的值 第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数.每行相邻两数之间用一空格分隔. [输出]: 仅一个整数,为a*b矩阵中所有“n*n正方形区域中的最大整数和最小整数的差值”的最小值. [输入样例] 5 4 2 1 2 5 6 0 17 16 0 16 17

HAOI2007 理想的正方形 单调队列

单调队列 by   GeneralLiu 滑动窗口是比较裸的单调队列 理想的正方形   就拔高了一个层次(多了一维) 有一个a*b的整数组成的矩阵 现请你从中找出一个n*n的正方形区域 使得该区域所有数中的最大值和最小值的差最小 只写MAX了,MIN一个道理,懒 不写了 先横着跑单调队列 维护许多长度为 n 的 横着的MAX 存在数组 map1[][] 中 再对数组 map1[][] 竖着跑单调队列 就维护了 许多 n*n 的 矩阵的MAX MIN同理 在竖着跑单调队列时 顺便MAX 与 MIN

【HAOI2007】理想的正方形

[问题描述] 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小. [输入] 第一行为3个整数,分别表示a,b,n的值第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数.每行相邻两数之间用一空格分隔. [输出] 仅一个整数,为a*b矩阵中所有"n*n正方形区域中的最大整数和最小整数的差值"的最小值. [分析] 单调队列,先处理横行,再处理竖行. 1 #include <cstdlib> 2 #inclu

[HAOI2007]理想的正方形

题目描述 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小. 输入输出格式 输入格式: 第一行为3个整数,分别表示a,b,n的值 第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数.每行相邻两数之间用一空格分隔. 输出格式: 仅一个整数,为a*b矩阵中所有"n*n正方形区域中的最大整数和最小整数的差值"的最小值. 输入输出样例 输入样例#1: 5 4 2 1 2 5 6 0 17 16 0 16 17 2 1 2

【bzoj1047】[HAOI2007]理想的正方形 二维RMQ

题目描述 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小. 输入 第一行为3个整数,分别表示a,b,n的值第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数.每行相邻两数之间用一空格分隔.100%的数据2<=a,b<=1000,n<=a,n<=b,n<=1000 输出 仅一个整数,为a*b矩阵中所有“n*n正方形区域中的最大整数和最小整数的差值”的最小值. 样例输入 5 4 2 1 2 5 6 0 1

洛谷OJ 2216 理想的正方形 单调队列(二维)

https://www.luogu.org/problem/show?pid=2216 题意:给出a*b矩形 从中找到一个n*n正方形,其(最大值-最小值之差)最小,a,b<=1e3,n<=100暴力枚举正方形右下角,如何快速算出其最大值和最小值?先用单调队列预处理出ma[i][j] 表示(i,j)以第i行j列结尾长度为n的最大值/在枚举列之后,对同一个列,由于已经知道该列 每行长度为n的最值 则在次利用单调队列,从上往下扫描行,求出(i,j)为右下角的矩形的最值即可 #include <

bzoj1047理想的正方形

题目链接 纪念又双叒叕的一道暴力碾标算的题 我们考虑纯暴力 #include<iostream> #include<cstdio> #include<algorithm> using namespace std; int a,b,n; int map[1010][1010]; int ans=0x3f3f3f3f; int main() { scanf("%d%d%d",&a,&b,&n); for(int i=1;i<