[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<=100
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
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2
Sample Output
1
HINT
Source
Solution
开始想用二维$ST$表,查了题解发现有复杂度更低的,,,
由于限定了矩阵长和宽都为$n$,于是就有了滑动窗口最大最小值的感觉
将每个点开始向左$n$个点的最大最小值算出来,对行使用单调队列求最值
接着将每个点作为右下角的$n*n$的矩阵的最大最小值求出来,使用刚才算出来的值,对列使用单调队列求最值
这样我们就算出来了每一个点对应的矩形的最大值减最小值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 int q[1005], c[1005][1005], r[4][1005][1005]; 4 int main() 5 { 6 int a, b, n, front, back, ans = 1000000000; 7 scanf("%d%d%d", &a, &b, &n); 8 for(int i = 1; i <= a; ++i) 9 for(int j = 1; j <= b; ++j) 10 scanf("%d", &c[i][j]); 11 for(int i = 1; i <= a; ++i) 12 { 13 front = back = 0; 14 for(int j = 1; j <= b; ++j) 15 { 16 if(front != back && j - q[front + 1] == n) 17 ++front; 18 while(front != back && c[i][j] <= c[i][q[back]]) 19 --back; 20 q[++back] = j; 21 r[0][i][j] = c[i][q[front + 1]]; 22 } 23 front = back = 0; 24 for(int j = 1; j <= b; ++j) 25 { 26 if(front != back && j - q[front + 1] == n) 27 ++front; 28 while(front != back && c[i][j] >= c[i][q[back]]) 29 --back; 30 q[++back] = j; 31 r[1][i][j] = c[i][q[front + 1]]; 32 } 33 } 34 for(int j = n; j <= b; ++j) 35 { 36 front = back = 0; 37 for(int i = 1; i <= a; ++i) 38 { 39 if(front != back && i - q[front + 1] == n) 40 ++front; 41 while(front != back && r[0][i][j] <= r[0][q[back]][j]) 42 --back; 43 q[++back] = i; 44 r[2][i][j] = r[0][q[front + 1]][j]; 45 } 46 front = back = 0; 47 for(int i = 1; i <= a; ++i) 48 { 49 if(front != back && i - q[front + 1] == n) 50 ++front; 51 while(front != back && r[1][i][j] >= r[1][q[back]][j]) 52 --back; 53 q[++back] = i; 54 r[3][i][j] = r[1][q[front + 1]][j]; 55 } 56 } 57 for(int i = n; i <= a; ++i) 58 for(int j = n; j <= b; ++j) 59 ans = min(ans, r[3][i][j] - r[2][i][j]); 60 printf("%d\n", ans); 61 return 0; 62 }