codevs#3147[题解]矩阵乘法2

题目描述 Description

给出两个n*n的矩阵,m次询问它们的积中给定子矩阵的数值和。

*为防止卡评测,已减小数据范围并调低时限。

输入描述 Input Description

第一行两个正整数n,m。
接下来n行,每行n个非负整数,表示第一个矩阵。
接下来n行,每行n个非负整数,表示第二个矩阵。
接下来m行,每行四个正整数a,b,c,d,表示询问第一个矩阵与第二个矩阵的积中,
以第a行第b列与第c行第d列为顶点的子矩阵中的元素和。

输出描述 Output Description

对每次询问,输出一行一个整数,表示该次询问的答案。

样例输入 Sample Input

3 2
1 9 8
3 2 0
1 8 3
9 8 4
0 5 15
1 9 6
1 1 3 3
2 3 1 2

样例输出 Sample Output

661
388

数据范围及提示 Data Size & Hint

【数据规模和约定】

对40%的数据满足,n <= 100,m <= 1000。
对100%的数据满足,n <= 800,m <= 10000,输入数据中矩阵元素 < 100,a,b,
c,d <= n。

数据有梯度。


80′暴力:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
struct Mart
{
	int a[801][801];
}A,B,C;
void Martix(Mart A,Mart B)
{
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			for(int k=1;k<=n;k++)
			{
				C.a[i][j]+=A.a[i][k]*B.a[k][j];
			}
		//	printf("%d ",C.a[i][j]);
		}
		//printf("\n");
	}
}
int x1,x2,y1,y2;
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			scanf("%d",&A.a[i][j]);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			scanf("%d",&B.a[i][j]);
	Martix(A,B);
	for(int k=1;k<=m;k++)
	{
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		long long ans = 0;
		if(x1>x2)swap(x1,x2);
		if(y1>y2)swap(y1,y2);
		for(int i=x1;i<=x2;i++)
		{
			for(int j=y1;j<=y2;j++)
			{
				ans += C.a[i][j];
			}
		}
		printf("%lld\n",ans);
	}
	return 0;	
}

设第一个矩阵的第i行第j列为a[i][j],第二个为b[i][j],根据矩阵乘法的定义,答案为∑(min(a,c)<=i<=max(a,c),min(b,d)<=j<=max(b,d))(∑(1<k<n)a[i][k]*b[k][j]),

时间复杂度为O(n^3),会超时.

所以我们必须将其变形:

加法结合律,得:(1<k<n)(∑(min(a,c)<=i<=max(a,c))(∑(min(b,d)<=j<=max(b,d))(a[i][k]*b[k][j]))),

乘法结合侓,得:(1<k<n)(((min(a,c)<=i<=max(a,c))a[i][k])*(∑(min(b,d)<=j<=max(b,d))b[k][j])),

时间复杂度为降至O(n^2),但由于要计算m次,还是会超时.

但我们可以发现,(min(a,c)<=i<=max(a,c))a[i][k]和∑(min(b,d)<=j<=max(b,d))b[k][j]可以用前缀和来求,

于是时间复杂度再降至O(n),但要额外加上与处理的O(n^2),

所以总时间复杂度为O(mn+n^2).

注意:由于空间限制是64000KB,所以,获得前缀和后不能保留矩阵a和矩阵b,而且,虽然变量类型要用长整型,但是前缀和只能用整型,用长整型的只能是答案.

附代码:

#include<iostream>

using namespace std;

int n,m,i,j,matrix1[2001][2001]={},matrix2[2001][2001]={},a,b,c,d;

long long answer;

int main ()

{

    cin>>n>>m;

    for (i=1;i<=n;i++)

    for (j=1;j<=n;j++)

    scanf("%d",&matrix1[i][j]);

    for (i=1;i<=n;i++)

    for (j=1;j<=n;j++)

    scanf("%d",&matrix2[i][j]);

    for (i=1;i<=n;i++)

    for (j=1;j<=n;j++)

    matrix1[i][j]+=matrix1[i-1][j];

    for (i=1;i<=n;i++)

    for (j=1;j<=n;j++)

    matrix2[i][j]+=matrix2[i][j-1];

    for (i=1;i<=m;i++)

    {

        scanf("%d %d %d %d",&a,&b,&c,&d);

        answer=0;

        for (j=1;j<=n;j++)

        answer+=(long long)(matrix1[max(a,c)][j]-matrix1[min(a,c)-1][j])*(long long)(matrix2[j][max(b,d)]-matrix2[j][min(b,d)-1]);

        cout<<answer<<endl;

    }

    return 0;

} 


ACcode:100scores.


#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
struct Mart
{
	int a[801][801];
}A,B;
int x1,x2,y1,y2;
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			scanf("%d",&A.a[i][j]);
			A.a[i][j]+=A.a[i-1][j];
		}
			
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			scanf("%d",&B.a[i][j]);
			B.a[i][j]+=B.a[i][j-1];
		}
	for(int k=1;k<=m;k++)
	{
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		long long ans = 0;
		if(x1>x2)swap(x1,x2);
		if(y1>y2)swap(y1,y2);
		for(int j=1;j<=n;j++)
		ans+=(A.a[x2][j]-A.a[x1-1][j])*(B.a[j][y2]-B.a[j][y1-1]);
		printf("%lld\n",ans);
	}
	return 0;	
}



你可能感兴趣的:(优化,矩阵,前缀和)