一道矩阵乘法的题目,做了这道题,让我稍微开拓了一点点思维。题意很简单,给你一个函数,如果x <10,f(x) = x;
如果x > 10, f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-10); a0,a1。。。。a9是常数,题目输入会给出。
做这个题目首先了解下矩阵。有两个矩阵A B,他们的乘积也是一个矩阵C,c[i][j]等于A矩阵第i行与B矩阵的第j列每个元素乘积的和。
例如A = a1 a2 B = b1 b2 ,那么c = (a1 * b1 + a2 * b3) (a1 *b2 + a2 * b4)
a3 a4 b3 b4 (a3 * b1 + a4 * b3) (a3 * b2 + a4 * b4)
所以两个矩阵A B能相乘,必须要A的列数等于B的行数。
将递推式化为矩阵 ,为了简便,假设只有4个元素:
a0 a1 a2 a3 f(x-1) f(x)
A = 1 0 0 0 B = f(x-2) 他们相乘得到矩阵c = f(x-1)
0 1 0 0 f(x-3) f(x-2)
0 0 1 0 f(x-4) f(x-3)
可以看到,A B相乘都能得到一个新的矩阵c,c比b中多了一个未知量f(x),再将A与c相乘,又可以得到一个新的矩阵d,d中比c多了一个f(x+1)
这样连乘几次,就能得到f(n)
同时矩阵乘法满足结合律,我们可以先将A连乘n - 10次,相当于A^(n-10)再乘以矩阵B,就可以得到我们要的答案了。
再简单的讲讲快速幂.
如果b是偶数,a^b是不是等于 (a^2)^(b/2)?例如2的8次方是不是等于2的平方的4次方?(2^8 = (2^2)^4 = 4^4),这样每次就可以将b减小一般,时间复杂度为O(logb)
如果b是奇数,那么我们就让结果ans 乘以一个a,就消去了a的一个次方
#include<stdio.h>
#include<memory>
using namespace std;
int a[10][10],ans[10][10],m;
void multi(int a[10][10],int b[10][10])//做矩阵A与矩阵A的乘法
{
int c[10][10];
for(int i = 0;i < 10;++i)
for(int j = 0;j < 10;++j)
{
int sum = 0;
for(int k = 0;k < 10;++k)
sum += a[i][k] * b[k][j];
c[i][j] = sum % m;
}
memcpy(a,c,sizeof(c));
}
int main()
{
int n,c[10];
while(scanf("%d%d",&n,&m) != EOF)
{
for(int i = 0;i < 10;++i)
scanf("%d",&c[i]);
memset(a,0,sizeof(a));
memset(ans,0,sizeof(ans));
for(i = 0;i < 10;++i)
a[0][i] = c[i];
for(i = 1;i < 10;++i)
{
a[i][i-1] = 1;
ans[i][i] = 1;
}
ans[0][0] = 1;
if(n < 10)
{
printf("%d\n",n);
continue;
}
n -= 9;
while(n > 1)
{
if(n&1)//如果n是奇数,就让结果乘以a
multi(ans,a);
multi(a,a);//让a * a,相当于 a = a*a
n >>= 1;
}
multi(ans,a);
int sum = 0;
for(i = 0;i < 10;++i)
{
sum += ans[0][i] * (10 - i - 1);
sum %= m;
}
printf("%d\n",sum%m);
}
return 0;
}