文章目录
- 题目大意
- 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;
}