P9905 [COCI 2023/2024 #1] AN2DL 题解

题目传送门

题目大意

给定 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;
}

总结

这道题就是单调队列,只不过增加了一维而已,还是可以轻松搞定。

  • 16
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值