矩阵求和

矩阵求和

难度级别:A; 编程语言:不限;运行时间限制:3000ms; 运行空间限制:256000KB; 代码长度限制:2000000B

试题描述

矩阵求和

输入

第一行n和m,表示行数和列数
接下来n行表示矩阵
再来一行一个整数q,表示询问个数
接下来q行,每行四个整数x1,y1,x2,y2表示左上右下的坐标

输出

对于每个询问输出一个整数表示该矩阵范围内的整数和

输入示例

5 5
1 2 5 4 1
1 1 7 6 8
8 7 9 5 2
4 4 4 1 8
5 10 11 13 7
3
2 1 4 3
2 2 5 5
4 1 5 4

输出示例

45
103
52

其他说明

n,m < 2001
q < 100001
矩阵内所有数小于2000

代码:

 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 }
View Code

代码分析:

有三种方法:

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 }
View Code

这个方法显然效率太低,两次循环太耗费时间了,所以要考虑运用前缀和的办法了。

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 }
View Code

这个方法不用两次循环,可还是有一次循环,但这还不能令人满意,有没有更快的方法呢?

分析上两个方法,我们发现:

这是第一个方法,它是以单个元素来求解的(以点做前缀和的)。一步步来累加。

第二个方法,前缀和,相当于是以行做单位求解。

既然,以行做前缀和的有了,    以点做前缀和的有了,那能不能以面做前缀和呢?

分析如下:

如此,推算赋值公式就如韦恩图那样:

 

把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]是从坐标(11)开始的,所以要减去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 }
View Code

这个方法只用一次运算便求出了解,所以它是三个方法中最快的方法。

posted @ 2016-05-24 19:21  wxjor  阅读(3088)  评论(0编辑  收藏  举报