文章目录
- 题目大意
- 1.输入格式
- 2.输出格式
- 3.数据范围与约定
- 思路
- 维护每一行区间
- 维护每一列区间
- 维护区间最大值
- code↓
- 完结撒花( ̄▽ ̄) /
题目大意
给定 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 的子矩阵的元素最大值。
1.输入格式
第一行两个整数 n , m n,m n,m 表示矩阵的高和宽。
接下来 n n n 行每行 m m m 个整数,表示矩阵 A A A。
最后一行两个整数 r , s r,s r,s。
2.输出格式
n − r + 1 n-r+1 n−r+1 行,每行 m − s + 1 m-s+1 m−s+1 个数,第 i i i 行 j j j 列的数表示以 ( i , j ) (i,j) (i,j) 为左上角的 r × s r\times s r×s 的子矩阵元素的最大值,即 max i ≤ x ≤ i + r − 1 , j ≤ y ≤ j + s − 1 A x , y \max\limits_{i\leq x\leq i+r-1,j\leq y\leq j+s-1}A_{x,y} i≤x≤i+r−1,j≤y≤j+s−1maxAx,y。
3.数据范围与约定
对于 100 % 100\% 100% 的数据, 1 ≤ n , m ≤ 4000 1\leq n,m\leq 4000 1≤n,m≤4000, ∣ A i , j ∣ ≤ 10000 \lvert A_{i,j}\rvert\leq 10000 ∣Ai,j∣≤10000, 1 ≤ r ≤ n 1\leq r\leq n 1≤r≤n, 1 ≤ s ≤ m 1\leq s\leq m 1≤s≤m。
子任务 | 特殊性质 | 分值 |
---|---|---|
1 1 1 | n , m ≤ 40 n,m\leq 40 n,m≤40, r = n r=n r=n, s = m s=m s=m | 12 12 12 |
2 2 2 | n , m ≤ 40 n,m\leq 40 n,m≤40 | 17 17 17 |
3 3 3 | n , m ≤ 1000 n,m\leq 1000 n,m≤1000 | 25 25 25 |
4 4 4 | 无特殊性质 | 56 56 56 |
思路
这道题我们可以用单调队列
来维护矩阵最大值
和矩阵最小值
先维护每一排的区间 ( i ∼ i + r ) (i\sim i+r) (i∼i+r)的最大值
可以用code来输出区间的起始值↓
#include <bits/stdc++.h>
using namespace std;
int main(){
int n,m,r,s;
cin>>n>>m>>r>>s;
for(int i=1;i<=n-r+1;i++){
for(int j=1;j<=m-s+1;j++){
cout<<"("<<i<<","<<j<<")"<<endl;
}
}
return 0;
}
运行结果如下↓
此图中的
(
x
,
y
)
(x,y)
(x,y)就是区间
的起始点
例如
(
2
,
3
)
(2,3)
(2,3)就如下图↓
图中用红色方框
框起来的便是
(
2
,
3
)
(2,3)
(2,3)表示的整个区间
其中
×
\times
×标起来的地方便是区间起始点
,也就是
(
2
,
3
)
(2,3)
(2,3)
维护每一行区间
我们要维护
的每一列的最大值
,而每一列区间
的起始点
可用代码输出↓
#include <bits/stdc++.h>
using namespace std;
int main(){
int n,m,r,s;
cin>>n>>m>>r>>s;
for(int i=1;i<=n;i++){
for(int j=1;j<=m-s+1;j++){
cout<<"("<<i<<","<<j<<")"<<endl;
}
}
return 0;
}
运行结果如下↓
在如下图可用
×
\times
×表示,下图用
×
\times
×依次表示了
(
1
,
1
)
(1,1)
(1,1),
(
2
,
2
)
(2,2)
(2,2),
(
3
,
3
)
(3,3)
(3,3),
(
4
,
1
)
(4,1)
(4,1)横排区间
所在的起始点
维护每一列区间
我们要维护
的每一列区间的最大值
,而每一列区间
的起始点
可用代码输出↓
#include <bits/stdc++.h>
using namespace std;
int main(){
int n,m,r,s;
cin>>n>>m>>r>>s;
for(int i=1;i<=n;i++){
for(int j=1;j<=m-s+1;j++){
cout<<"("<<i<<","<<j<<")"<<endl;
}
}
return 0;
}
在如下图可用
×
\times
×表示,下图用
×
\times
×依次表示了
(
1
,
1
)
(1,1)
(1,1),
(
2
,
2
)
(2,2)
(2,2),
(
3
,
3
)
(3,3)
(3,3),
(
1
,
4
)
(1,4)
(1,4),
(
2
,
5
)
(2,5)
(2,5)竖列区间
所在的起始点
维护区间最大值
整个区间的最大值都会集中在code↓
for(int i=1;i<=n-r+1;i++)
for(int j=1;j<=m-s+1;j++)
如下图↓
图中的
×
\times
×表示的就是区间最大值所在的位置
只需要将纵列
的单调队列
与横排
的单调队列
进行合并即可,其中ans[]数组
便是用来存储区间最大值
的数组
code↓
#include <bits/stdc++.h>
using namespace std;
int n,m,a[4005][4005],ans[4005][4005],r,s,b[4005][4005];//ans是答案数组,n行,m列,求r行,s列的矩阵最大值
int main(){
cin>>n>>m;
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];//输入初始矩阵
}
}
cin>>r>>s;//输入需要求的矩阵的行数和列数
for(int i=1;i<=n;i++){
deque<int> mx;//定义一个单调队列,用来存储横排的序号
for(int j=1;j<=m;j++){
while(!mx.empty()&&a[i][mx.back()]<=a[i][j]) mx.pop_back();//判断是否非空,满足单调性
mx.push_back(j);//将j给压入mx这个队列
while(!mx.empty()&&mx.front()<=j-s) mx.pop_front();//队列的头不在这个区间内,将它弹出
if(j>=s) b[i][j-s+1]=a[i][mx.front()]; //求出a[i][j]~a[i][j+s]这个区间中的最大值
}
}
for(int j=1;j<=m-s+1;j++){//区间的竖列起点是1~(m-s+1)
deque<int> mn;//定义一个单调队列,用来存储竖列的序号
for(int i=1;i<=n;i++){
while(!mn.empty()&&b[mn.back()][j]<=b[i][j]) mn.pop_back();//判断是否非空,用竖列的序号去满足单调性
mn.push_back(i);//将j压入mn这个序列
while(!mn.empty()&&mn.front()<=i-r) mn.pop_front();//求出竖列中的区间最大值
if(i>=r) ans[i-r+1][j]=b[mn.front()][j];//这里是求出数列中的区间最大值,ans数组来进行存储,最后输出就行
}
}
for(int i=1;i<=n-r+1;i++){//横列开始的起点
for(int j=1;j<=m-s+1;j++){//竖列开始的起点
cout<<ans[i][j]<<' ';//输出答案
}
cout<<endl;
}
return 0;
}