P2216 [HAOI2007] 理想的正方形
题目描述
有一个 a×ba \times ba×b 的整数组成的矩阵,现请你从中找出一个 n×nn \times nn×n 的正方形区域,使得该区域所有数中的最大值和最小值的差最小。
输入格式
第一行为 333 个整数,分别表示 a,b,na,b,na,b,n 的值。
第二行至第 a+1a+1a+1 行每行为 bbb 个非负整数,表示矩阵中相应位置上的数。每行相邻两数之间用一空格分隔。
输出格式
仅一个整数,为 a×ba \times ba×b 矩阵中所有“ n×nn \times nn×n 正方形区域中的最大整数和最小整数的差值”的最小值。
输入输出样例 #1
输入 #1
5 4 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2
输出 #1
1
说明/提示
矩阵中的所有数都不超过 1,000,000,0001,000,000,0001,000,000,000。
20%20\%20% 的数据 2≤a,b≤100,n≤a,n≤b,n≤102 \le a,b \le 100,n \le a,n \le b,n \le 102≤a,b≤100,n≤a,n≤b,n≤10。
100%100\%100% 的数据 2≤a,b≤1000,n≤a,n≤b,n≤1002 \le a,b \le 1000,n \le a,n \le b,n \le 1002≤a,b≤1000,n≤a,n≤b,n≤100。
解析
只考虑最大值,最小值同理。
考虑 n2n^2n2 的正方形中,最大值其实就是这 nnn 列最大值中的最大值。
我们先用枚举方法,将每个数及其下方共计 nnn 个数最大值记为 Maxi,jMax_{i,j}Maxi,j,这可以在 O(abn)O(abn)O(abn) 的时间复杂度内做到。对于每一个行,只需要求连续 nnn 个 Maxi,jMax_{i,j}Maxi,j 最大值即可,这暴力就好了。
此外,如果两个区间最大值都用单调队列亦可解决此题,但在这里小题大做了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,a,b;
int num[1005][1005],Max[1005][1005],Min[1005][1005];
int fr,end,que[1005],bh[1005];
int ans = 10000000001;
signed main() {scanf("%lld %lld %lld",&a,&b,&n);for(int i = 1;i <= a;i++) {for(int j = 1;j <= b;j++) scanf("%lld",&num[i][j]);}for(int i = 1;i <= a - n + 1;i++) {for(int j = 1;j <= b;j++) {Max[i][j] = -1,Min[i][j] = 10000000001;for(int k = 0;k < n;k++) {Max[i][j] = max(Max[i][j],num[i + k][j]);Min[i][j] = min(Min[i][j],num[i + k][j]);}}}for(int i = 1;i <= a - n + 1;i++) {for(int j = 1;j <= b - n + 1;j++) {int M_max = -1,M_min = 100000000001;for(int k = 0;k < n;k++) {M_max = max(M_max,Max[i][j + k]);M_min = min(M_min,Min[i][j + k]);}//printf("(%lld,%lld):%lld %lld\n",i,j,M_max,M_min);ans = min(ans,M_max - M_min);}}printf("%lld\n",ans);return 0;
}
