题目链接
:P9905 [COCI 2023/2024 #1] AN2DL
大体题意
给定 n , m , r , s n,m,r,s n,m,r,s 和一个大小为 n ∗ m n*m n∗m 的矩阵 A A A,求每个大小为 r ∗ s r*s r∗s 的子矩阵中最大值
第一行两个整数 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 3∗3 的 A A A 矩阵中只有一个大小为 3 ∗ 3 3*3 3∗3 的矩阵(本身),其中最大值为 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;
}