题目传送门
题目大意
给定 n , m , r , s n,m,r,s n,m,r,s 和一个 n × m n\times m n×m 的整数矩阵 A A A,求它每个 r × s r\times s r×s 的子矩阵的元素最大值。
解题思路
这道题目我们一看,咦?怎么有点像滑动窗口呢?于是就想到了单调队列。
我们定义一个单调队列
q
q
q,对于每一个数,我们判断 q.front()
是否小于等于
n
o
w
now
now,如果是,那么
n
o
w
now
now 一定要存下来,因为他不仅可以在队列里多待一会且贡献大于等于 q.front()
,所以我们循环弹出队头,直到 q.front()
大于当前数,然后入队清除过时的元素即可。
单调队列考得较少,但是一考就要命,建议自学一下。
由于是二维的,所以需要跑两遍。
卡常技巧,使用快读,getchar()
的速度要比 cin
快得多,还可以用二进制优化即
k = k * 10 + (c - 48)
写成了
k = (k << 1) + (k << 3) + (c ^ 48)
快写主要是递归,同样 putchar()
的速度也比 cout
快得多。
CODE:
#include <stdio.h>
#include <string.h>
int a[4001][4001], ans[4001][4001], b[4001][4001], q[4001];
inline int read() {
int f = 1, k = 0;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
k = (k << 1) + (k << 3) + (c ^ 48);
c = getchar();
}
return f * k;
}
inline void write(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
return;
}
int main() {
int n = read(), m = read();
int i, j;
for (i = 1; i <= n; i++)
for (j = 1; j <= m; j++)
a[i][j] = read();
int r = read(), s = read();
int k = n - r + 1, k2 = m - s + 1;
for (i = 1; i <= n; i++) {
int hh = 0, tt = -1;
for (j = 1; j <= m; j++) {
while (hh <= tt && a[i][q[tt]] <= a[i][j])
tt--;
q[++tt] = j;
while (hh <= tt && q[hh] <= j - s)
hh++;
if (j >= s)
b[i][j - s + 1] = a[i][q[hh]];
}
memset(q, 0, sizeof(q));
}
for (j = 1; j <= k2; j++) {
int hh = 0, tt = -1;
for (i = 1; i <= n; i++) {
while (hh <= tt && b[q[tt]][j] <= b[i][j])
tt--;
q[++tt] = i;
while (hh <= tt && q[hh] <= i - r)
hh++;
if (i >= r)
ans[i - r + 1][j] = b[q[hh]][j];
}
memset(q, 0, sizeof(q));
}
for (i = 1; i <= k; i++, puts(""))
for (j = 1; j <= k2; j++)
write(ans[i][j]), putchar(' ');
return 0;
}
总结
这道题就是单调队列,只不过增加了一维而已,还是可以轻松搞定。