矩阵乘法

设A矩阵是一个n*m的矩阵,B矩阵是一个m*p的矩阵,那么A与B相乘就得到n*p的C矩阵。

而Cij就等于A的第i行和B第j列对应的每个数相乘再相加的和。

只要满足第一个矩阵的列数与第二个矩阵的行数相同就行。

矩阵乘法有一些基本性质:

1、乘法结合律:(A*B)*C=A*(B*C)

2、乘法左右分配律:(A+B)*C=A*C+B*C或A*C+B*C=(A+B)*C

3、对称乘的结合律:k(A*B)=(k*A)*B=A*(k*B)

矩阵乘法一般不满足交换律。

矩阵乘法一个重要的作用就是加速递推公式,就拿斐波那契数列为例,给出前两项求第n项,那么可以先造一个1*2的A矩阵,分别放入第二项和第一项,再造出一个2*2的B矩阵,除了右下角是0,其他三个数都是1;那么将这两个矩阵相乘我们可以得到一个1*2的矩阵而这个矩阵的第一位就是f[1]+f[2],也就是f[3],第二位是f[2]。相当于把A矩阵往下推了一位。那么只要把A矩阵乘上n-2次B矩阵就能得到第n项了!

而把B矩阵乘上n-2次我们可以想到用快速幂,那么就说到了矩阵快速幂。

矩阵快速幂和常规快速幂差不多,只是把数与数相乘换成了矩阵与矩阵相乘,这里有两种方法:一是重载运算符,二是写一个矩阵乘法的函数(其实还可以暴力的把快速幂里的乘法都改成矩阵乘法)。

常规快速幂里的初始值为1,那么矩阵快速幂里的初始矩阵也应该具有1的性质(与任何矩阵相乘得到的矩阵是那个矩阵本身),而这个矩阵就是单位矩阵,单位矩阵是只有从左上到右下一条对角线为1,其他全部是0的正方形矩阵,可以发现这个矩阵乘任何矩阵都是那个矩阵本身。

定义完初始矩阵,我们还需要一个用来储存乘法结果的矩阵,值得注意的是只有当矩阵乘法全部乘完才能把这个矩阵的值付回去。

至于需要乘的B矩阵要根据题来自己创造,有的较难的题的递推式中会有常数。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
//f[n]=a1*f[n-1]+a2*f[n-2]+...+ak*f[n-k];
int a[100];//系数 
int f[100];//递推矩阵 
int g[100][100];//转移矩阵 
int s[100][100];//单位矩阵 
int b[100];//前k项 
int t[100][100];//结果矩阵 
int q[100];//结果矩阵 
int K; 
int N;
int ans=0;
void quickpow(int g[][100],int n)
{
	memset(s,0,sizeof(s));
	memset(t,0,sizeof(t));
	for(int i=1;i<=K;i++)
	{
		s[i][i]=1;
	}
	while(n)
	{
		if(n%2==1)
		{
			for(int i=1;i<=K;i++)
			{
				for(int j=1;j<=K;j++)
				{
					for(int k=1;k<=K;k++)
					{
						t[i][j]+=s[i][k]*g[k][j];
					}
				}
			}
			for(int i=1;i<=K;i++)
			{
				for(int j=1;j<=K;j++)
				{
					s[i][j]=t[i][j];
					t[i][j]=0;
				}
			}
		}
		n=n/2;
		for(int i=1;i<=K;i++)
		{
			for(int j=1;j<=K;j++)
			{
				for(int k=1;k<=K;k++)
				{
					t[i][j]+=g[i][k]*g[k][j];
				}
			}
		}
		for(int i=1;i<=K;i++)
		{
			for(int j=1;j<=K;j++)
			{
				g[i][j]=t[i][j];
				t[i][j]=0;
			}
		}
	}
}
int main()
{
	scanf("%d",&K);
	scanf("%d",&N);
	memset(g,0,sizeof(g));
	for(int i=1;i<=K;i++)
	{
		scanf("%d",&a[i]);
		g[1][i]=a[i];
	}
	for(int i=1;i<=K;i++)
	{
		scanf("%d",&b[i]);		
		f[K-i+1]=b[i];
	}
	if(N<=K)
	{
		printf("%d",b[N]);
	}
	for(int i=2;i<=K;i++)
	{
		g[i][i-1]=1;
	}
	quickpow(g,N-K);
	for(int i=1;i<=K;i++)
	{
		ans+=s[1][i]*f[i];
	}
	printf("%d",ans);
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值