BZOJ 1047: [HAOI2007]理想的正方形【堆||单调序列】

1047: [HAOI2007]理想的正方形

Time Limit: 10 Sec Memory Limit: 162 MB

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

题解
这题其实不是很难,我们很容易想到先将每行n个压成一个点,然后再把压缩过的矩阵每列压成一个点(反过来也可以),然后扫一下答案就可以了。
那么接下来就是答案怎么得,这不就很简单了吗?
下面两种选择:
堆:

O(ablog2a+ablog2b)

单调序列:
O(ab2)

代码如下:

#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,K,que[1005],mp[1005][1005],g[2][1005][1005],t[2][1005][1005],ans=1<<30;
int work00(int x){//列取大的 
    int hd=1,tl=0;
    for(int y=1;y<K;y++){
        while(mp[x][que[tl]]<mp[x][y]&&tl>=hd) tl--;
        que[++tl]=y;
    }
    for(int y=K;y<=m;y++){
        while(mp[x][que[tl]]<mp[x][y]&&tl>=hd) tl--;
        que[++tl]=y;
        while(y-que[hd]>=K&&hd<tl) hd++;
        g[0][x][y]=mp[x][que[hd]];
    }
}
int work01(int x){//列取小的 
    int hd=1,tl=0;
    for(int y=1;y<K;y++){
        while(mp[x][que[tl]]>mp[x][y]&&tl>=hd) tl--;
        que[++tl]=y;
    }
    for(int y=K;y<=m;y++){
        while(mp[x][que[tl]]>mp[x][y]&&tl>=hd) tl--;
        que[++tl]=y;
        while(y-que[hd]>=K&&hd<tl) hd++;
        g[1][x][y]=mp[x][que[hd]];
    }
}
int work10(int y){//行取大的 
    int hd=1,tl=0;
    for(int x=1;x<K;x++){
        while(g[0][que[tl]][y]<g[0][x][y]&&tl>=hd) tl--;
        que[++tl]=x;
    }
    for(int x=K;x<=n;x++){
        while(g[0][que[tl]][y]<g[0][x][y]&&tl>=hd) tl--;
        que[++tl]=x;
        while(x-que[hd]>=K&&hd<tl) hd++;
        t[0][x][y]=g[0][que[hd]][y];
    }
}
int work11(int y){//行取小的 
    int hd=1,tl=0;
    for(int x=1;x<K;x++){
        while(g[1][que[tl]][y]>g[1][x][y]&&tl>=hd) tl--;
        que[++tl]=x;
    }
    for(int x=K;x<=n;x++){
        while(g[1][que[tl]][y]>g[1][x][y]&&tl>=hd) tl--;
        que[++tl]=x;
        while(x-que[hd]>=K&&hd<tl) hd++;
        t[1][x][y]=g[1][que[hd]][y];
    }
}
int main(){
    scanf("%d%d%d",&n,&m,&K);
    if(n==0){printf("0\n");return 0;}
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++) scanf("%d",&mp[i][j]);
    for(int i=1;i<=n;i++) work00(i),work01(i);
    for(int i=1;i<=m;i++) work10(i),work11(i);
    for(int i=K;i<=n;i++)
    for(int j=K;j<=m;j++) ans=min(t[0][i][j]-t[1][i][j],ans);
    printf("%d\n",ans);
    return 0;
}
posted @ 2018-01-25 16:18  XSamsara  阅读(156)  评论(0编辑  收藏  举报