[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

Sample Output

1

HINT

Source

Solution

  开始想用二维$ST$表,查了题解发现有复杂度更低的,,,

  由于限定了矩阵长和宽都为$n$,于是就有了滑动窗口最大最小值的感觉

  将每个点开始向左$n$个点的最大最小值算出来,对行使用单调队列求最值

  接着将每个点作为右下角的$n*n$的矩阵的最大最小值求出来,使用刚才算出来的值,对列使用单调队列求最值

  这样我们就算出来了每一个点对应的矩形的最大值减最小值

 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 }
View Code

 

posted @ 2016-07-13 18:49  CtrlCV  阅读(973)  评论(0编辑  收藏  举报