差分矩阵
题目分析
输入一个n行m列的整数矩阵,再输入q个操作,每个操作包含五个整数x1, y1, x2, y2, c,其中(x1, y1)和(x2, y2)表示一个子矩阵的左上角坐标和右下角坐标。每个操作都要将选中的子矩阵中的每个元素的值加上c。请你将进行完所有操作后的矩阵输出。
输入格式
第一行包含整数n,m,q。接下来n行,每行包含m个整数,表示整数矩阵。接下来q行,每行包含5个整数x1, y1, x2, y2, c,表示一个操作。
输出格式
共 n 行,每行 m 个整数,表示所有操作进行完毕后的最终矩阵。
数据范围
n,m<=1000;
输入样例
3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 1
输出样例
2 3 4 1
4 3 4 1
2 2 2 2
这道题其实有两种解法,一种是暴力枚举另外一种是差分,暴力枚举的时间复杂度比较大,所以我比较推荐差分,下面我把这两种方法都写下来,供大家参考。
- 暴力枚举
就是把(x,y)到(x1,y1)范围内的值都枚举出来,然后把枚举出来的值加上c.
#include<iostream>
const int N=1e3+10;
using namespace std;
int str[N][N];
int main(){
int n,m,q,x,y,x1,y1,c;
cin>>n>>m>>q;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)
cin>>str[i][j];
}
while(q--){
cin>>x>>y>>x1>>y1>>c;
for(int i=x;i<=x1;i++){//枚举(x,y)到(x1,y1)之间的数据并令它们加c
for(int j=y;j<=y1;j++)
str[i][j]=str[i][j]+c;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)
cout<<str[i][j]<<" ";
cout<<endl;
}
return 0;
}
- 差分法
这种方法用到了前缀和与差分法。
前缀和
前缀和是指某序列的前n项和,可以把它理解为数学上的数列的前n项和,而差分可以看成前缀和的逆运算。合理的使用前缀和与差分,可以将某些复杂的问题简单化。
差分
差分可以看成前缀和的逆运算。
差分数组:
首先给定一个原数组str:str[1], str[2], str[3],,,,,, str[n];
然后我们构造一个数组barr: arr[1], arr[2], arr[3],,,,,, arr[i];
使得 str[i] = arr[1] + arr[2] + arr[3] + ,,,,,, + arr[i]
也就是说,str数组是arr数组的前缀和数组,反过来我们把arr数组叫做str数组的差分数组。
针对这个题,首先先设置一个数组str用来存储元数据,在构造一个arr数组用来表示数组str的差分数组,str是arr的前缀和数组。
差分数组的构造方法:
arr[x][y]+=c;
arr[x1+1][y]-=c;
arr[x][y1+1]-=c;
arr[x1+1][y1+1]+=c;
代码如下:
#include<iostream>
const int N=1e3+10;
using namespace std;
int str[N][N],arr[N][N];
void pre(int x,int y,int x1,int y1,int c){//差分数组构造函数,以及子矩阵的元素加c
arr[x][y]+=c;
arr[x1+1][y]-=c;
arr[x][y1+1]-=c;
arr[x1+1][y1+1]+=c;
}
int main(){
int n,m,q,x,y,x1,y1,c;
cin>>n>>m>>q;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>str[i][j];
pre(i,j,i,j,str[i][j]);//构造数组str的差分数组
}
}
while(q--){
cin>>x>>y>>x1>>y1>>c;
pre(x,y,x1,y1,c);
}
for(int i=1;i<=n;i++){//求数组arr的前缀和,arr的前缀和表示最终的矩阵
for(int j=1;j<=m;j++){
arr[i][j]=arr[i][j]+arr[i-1][j]+arr[i][j-1]-arr[i-1][j-1];
cout<<arr[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
总的来说这道题考的知识点比较多,是中等水平的题,大家可以多练练这类题。