P9905 [COCI 2023/2024 #1] AN2DL【子矩阵】

题目链接P9905 [COCI 2023/2024 #1] AN2DL

大体题意

给定 n , m , r , s n,m,r,s n,m,r,s 和一个大小为 n ∗ m n*m nm 的矩阵 A A A,求每个大小为 r ∗ s r*s rs 的子矩阵中最大值

第一行两个整数 n , m n,m n,m 表示矩阵的高和宽
接下来 n n n 行每行 m m m 个整数,表示矩阵 A A A
最后一行两个整数 r , s r,s r,s

输入输出样例

输入 #1
3 3
1 1 2
2 3 4
4 3 2
3 3
输出 #1
4

解释: 3 ∗ 3 3*3 33 A A A 矩阵中只有一个大小为 3 ∗ 3 3*3 33 的矩阵(本身),其中最大值为 4 4 4

输入 #2
3 3
1 1 2
2 3 4
4 3 2
2 1
输出 #2
2 3 4
4 3 4

解释:
在这里插入图片描述

思路

注:前提知识:单调队列

区间最值显然可以想到单调队列
但是这是二维的矩阵,但是我们可以分开看
也就是每一行都先做一次单调队列求区间最值,即下图
在这里插入图片描述
此时我们发现每个子矩阵的最大值肯定都在这个子矩阵最右边一列上
所以我们在做一次纵向的单调队列,那么子矩阵中的最大一定在这个子矩阵的右下角
然后我们输出右下矩阵的数就行了

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m,r,s,a[4005][4005],maxn,head[4005],vis[4005],hail,tal;
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			scanf("%d",&a[i][j]); //输入A矩阵
	scanf("%d%d",&r,&s);
	for(int j=1;j<=n;j++) //横向的单调队列(自左向右)
	{
		hail=1,tal=1; //hail队头 tal队尾
		head[hail]=a[j][1]; //head单调队列
		vis[hail]=1; //把它的位置记下来,方便以后看还可不可以用
		for(int i=2;i<=m;i++)
		{
			if(i-vis[hail]>=s) hail++; //如果队头的元素已经在r*s的矩阵外,那么队头元素就用不到了
			while(a[j][i]>head[tal] && tal>=hail) tal--; //如果之前的元素没有这个元素大,那么它们肯定不会是之后的最大值
			head[++tal]=a[j][i]; //加入这个元素
			vis[tal]=i; //同15行把它的位置记下来
			a[j][i]=head[hail]; //这个位置前s个位置的最大值赋到a[j][i]上
		}
	}
	for(int j=s;j<=m;j++) //和前一个for循环一样,但是是纵向的单调队列(自上而下)
	{
		hail=1,tal=1;
		head[hail]=a[1][j];
		vis[hail]=1;
		for(int i=2;i<=n;i++)
		{
			if(i-vis[hail]>=r) hail++;
			while(a[i][j]>head[tal] && tal>=hail) tal--;
			head[++tal]=a[i][j];
			vis[tal]=i;
			a[i][j]=head[hail];
		}
	}
	for(int i=r;i<=n;i++)
	{
		for(int j=s;j<=m;j++) //此时每个矩阵的最大值一定在这个矩阵的右下角
			printf("%d ",a[i][j]); //输出最大值
		printf("\n");
	}
	return 0;
}
  • 17
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值