矩阵求和
|
代码:
1 #include<iostream> 2 3 int a,m,n,i,j,k,x1,y1,x2,y2; 4 5 long long sum,s[2002][2002]={0};//数组s用来储存前缀和 6 7 using namespace std; 8 9 int main() 10 11 { 12 13 scanf("%d%d",&m,&n); 14 15 for(i=1;i<=m;i++)//输入,赋值二维数组s 16 17 for(j=1;j<=n;j++) 18 19 { 20 21 scanf("%d",&a);//为了节省空间,以单个变量输入 22 23 s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a; 24 25 } 26 27 scanf("%d",&k); 28 29 while(k--) 30 31 { 32 33 scanf("%d%d%d%d",&x1,&y1,&x2,&y2); 34 35 sum=s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]; 36 37 printf("%lld\n",sum); 38 39 } 40 41 }
代码分析:
有三种方法:
1. 直接求解,按单个元素记录。因此,k次查询里每次都要有两个循环,一个横着在这个区间里扫描,另一个竖着扫描。
代码如下:
1 #include<iostream> 2 3 int a[2010][2010],m,n,i,j,k,b,c,d,e; 4 5 long long sum; 6 7 using namespace std; 8 9 int main() 10 11 { 12 13 scanf("%d%d",&m,&n); 14 15 for(i=1;i<=m;i++) 16 17 { 18 19 for(j=1;j<=n;j++) 20 21 scanf("%d",&a[i][j]); 22 23 } 24 25 cin>>k; 26 27 while(k--) 28 29 { 30 31 sum=0; 32 33 scanf("%d%d%d%d",&b,&c,&d,&e); 34 35 for(i=b;i<=d;i++)//两次循环 36 37 for(j=c;j<=e;j++) 38 39 sum+=a[i][j]; 40 41 printf("%lld\n",sum); 42 43 44 45 } 46 47 }
这个方法显然效率太低,两次循环太耗费时间了,所以要考虑运用前缀和的办法了。
2. 利用之前数组储存前缀和的办法,给这个二维数组每行都求一个前缀和,然后在查询时,用要求的区间当中每一行的从y1到y2的和,用s[x][y2]-s[x][y1-1](x为第x行)
所以采用这个方法效率比上一个方法更快。
代码:
1 #include<iostream> 2 3 int a,m,n,i,j,k,b,c,d,e,s[2002][2002]={0}; 4 5 long long sum; 6 7 int main() 8 9 { 10 11 scanf("%d%d",&m,&n); 12 13 for(i=1;i<=m;i++) 14 15 for(j=1;j<=n;j++) 16 17 { 18 19 scanf("%d",&a); 20 21 s[i][j]=s[i][j-1]+a; 22 23 } 24 25 scanf("%d",&k); 26 27 while(k--) 28 29 { 30 31 sum=0; 32 33 scanf("%d%d%d%d",&b,&c,&d,&e); 34 35 for(i=b;i<=d;i++) 36 37 sum+=(s[i][e]-s[i][c-1]); 38 39 printf("%lld\n",sum); 40 41 } 42 43 }
这个方法不用两次循环,可还是有一次循环,但这还不能令人满意,有没有更快的方法呢?
分析上两个方法,我们发现:
这是第一个方法,它是以单个元素来求解的(以点做前缀和的)。一步步来累加。
第二个方法,前缀和,相当于是以行做单位求解。
既然,以行做前缀和的有了, 以点做前缀和的有了,那能不能以面做前缀和呢?
分析如下:
如此,推算赋值公式就如韦恩图那样:
把s[i-1][j]和s[i][j-1]加起来,s[i-1][j-1]部分重叠了,所以要减去,在加上输入的a,就得到:
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a;
而输出就也像上面一样,s[x2][y2]是从坐标(1,1)开始的,所以要减去s[x2][y1-1]和s[x1-1][y2],s[x1-1][y1-1]被多减了,所以要加上。
代码:
1 #include<iostream> 2 3 long long sum,a,m,n,i,j,k,x1,y1,x2,y2,s[2002][2002]={0}; 4 5 using namespace std; 6 7 int main() 8 9 { 10 11 scanf("%d%d",&m,&n); 12 13 for(i=1;i<=m;i++) 14 15 for(j=1;j<=n;j++) 16 17 { 18 19 scanf("%d",&a); 20 21 s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a; 22 23 } 24 25 scanf("%d",&k); 26 27 while(k--) 28 29 { 30 31 scanf("%d%d%d%d",&x1,&y1,&x2,&y2); 32 33 sum=s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]; 34 35 printf("%lld\n",sum); 36 37 } 38 39 }
这个方法只用一次运算便求出了解,所以它是三个方法中最快的方法。