BZOJ1047: [HAOI2007]理想的正方形

Description

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

Input

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

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
复杂度明显是N2
那就跑单调队列,先横着跑,再竖着跑。。
//MT_LI
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
int d[1100][1100];
int a,b,n;
int mn[1100][1100],mx[1100][1100];
int minn[1100][1100],maxx[1100][1100];
struct line{
    int x,pos;
}list[510000];int head,tail;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void solve_max()
{
    for(int i=1;i<=a;i++)
    {
        head=1,tail=0;
        for(int j=1;j<=n;j++)
        {
            while(head<=tail&&list[tail].x<d[i][j])tail--;
            list[++tail].x=d[i][j],list[tail].pos=j;
        }
        mx[i][1]=list[head].x;
        for(int j=n+1;j<=b;j++)
        {
            while(head<=tail&&list[head].pos<j-n+1)head++;
            while(head<=tail&&list[tail].x<d[i][j])tail--;
            list[++tail].x=d[i][j],list[tail].pos=j;
            mx[i][j-n+1]=list[head].x;
        }
    }
    for(int j=n+1;j<=b+1;j++)
    {
        head=1,tail=0;
        for(int i=1;i<=n;i++)
        {
            while(head<=tail&&list[tail].x<mx[i][j-n])tail--;
            list[++tail].x=mx[i][j-n],list[tail].pos=i;
        }
        maxx[1][j-n]=list[head].x;
        for(int i=n+1;i<=a;i++)
        {
            while(head<=tail&&list[head].pos<i-n+1)head++;
            while(head<=tail&&list[tail].x<mx[i][j-n])tail--;
            list[++tail].x=mx[i][j-n];list[tail].pos=i;
            maxx[i-n+1][j-n]=list[head].x;
        }
    }
}
void solve_min()
{
    for(int i=1;i<=a;i++)
    {
        head=1,tail=0;
        for(int j=1;j<=n;j++)
        {
            while(head<=tail&&list[tail].x>d[i][j])tail--;
            list[++tail].x=d[i][j],list[tail].pos=j;
        }
        mn[i][1]=list[head].x;
        for(int j=n+1;j<=b;j++)
        {
            while(head<=tail&&list[head].pos<j-n+1)head++;
            while(head<=tail&&list[tail].x>d[i][j])tail--;
            list[++tail].x=d[i][j],list[tail].pos=j;
            mn[i][j-n+1]=list[head].x;
        }
    }
    for(int j=n+1;j<=b+1;j++)
    {
        head=1,tail=0;
        for(int i=1;i<=n;i++)
        {
            while(head<=tail&&list[tail].x>mn[i][j-n])tail--;
            list[++tail].x=mn[i][j-n],list[tail].pos=i;
        }
        minn[1][j-n]=list[head].x;
        for(int i=n+1;i<=a;i++)
        {
            while(head<=tail&&list[head].pos<i-n+1)head++;
            while(head<=tail&&list[tail].x>mn[i][j-n])tail--;
            list[++tail].x=mn[i][j-n];list[tail].pos=i;
            minn[i-n+1][j-n]=list[head].x;
        }
    }
}
int main()
{
    scanf("%d%d%d",&a,&b,&n);
    for(int i=1;i<=a;i++)
        for(int j=1;j<=b;j++)
            d[i][j]=read();
    solve_max();solve_min();
    int ans=1<<30;
    for(int i=1;i<=a-n+1;i++)
        for(int j=1;j<=b-n+1;j++)
            ans=min(ans,maxx[i][j]-minn[i][j]);
    printf("%d
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/MT-LI/p/9834548.html