BZOJ 1047: [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

解题方法:
 将每个点开始向左n个点的最大最小值算出来,对行使用单调队列求最值。接着将每个点作为右下角的n∗n的矩阵的最大最小值求出来,使用刚才算出来的值,对列使用单调队列求最值
这样我们就算出来了每一个点对应的矩形的最大值减最小值!!

代码如下:

#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int head, tail, que[N], maze[N][N], f[4][N][N];
void getmin(int &x, int y){
    if(y < x) x = y;
}
int main(){
    int a, b, n;
    while(scanf("%d%d%d", &a, &b, &n) != EOF){
        for(int i = 1; i <= a; i++){
            for(int j = 1; j <= b; j++){
                scanf("%d", &maze[i][j]);
            }
        }
        for(int i = 1; i <= a; i++){//对行做单调队列,维护这个点向左n个点的最大和最小
            head = tail = 0;
            for(int j = 1; j <= b; j++){//减
                while(head < tail && j - que[head] >= n) ++head;
                while(head < tail && maze[i][j] <= maze[i][que[tail - 1]]) --tail;
                que[tail++] = j;
                f[0][i][j]  = maze[i][que[head]];
            }
            head = tail = 0;
            for(int j = 1; j <= b; j++){//增
                while(head < tail && j - que[head] >= n) ++head;
                while(head < tail && maze[i][j] >= maze[i][que[tail - 1]]) --tail;
                que[tail++] = j;
                f[1][i][j] = maze[i][que[head]];
            }
        }
        for(int j = 1; j <= b; j++){ //对列做单调队列,相当于(i, j)点往上延伸n距离的最大值和最小值
            head = tail = 0;
            for(int i = 1; i <= a; i++){//减
                while(head < tail && i - que[head] >= n) ++head;
                while(head < tail && f[0][i][j] <= f[0][que[tail-1]][j]) --tail;
                que[tail++] = i;
                f[2][i][j] = f[0][que[head]][j];
            }
            head = tail = 0;
            for(int i = 1; i <= a; i++){//增
                while(head < tail && i - que[head] >= n) ++head;
                while(head < tail && f[1][i][j] >= f[1][que[tail-1]][j]) --tail;
                que[tail++] = i;
                f[3][i][j] = f[1][que[head]][j];
            }
        }
        int ans = 1000000000;
        for(int i = n; i <= a; i++){
            for(int j = n; j <= b; j++){
                getmin(ans, f[3][i][j] - f[2][i][j]);
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值