最近做动态规划时,遇到了一道题,需要矩阵乘法,这里总结一下(感谢 @The_useless)
首先,
即对于C矩阵来说,c[i][j]=A矩阵[i]行与B矩阵[j]列的乘积之和,Code:
matrix operator *(matrix a,matrix b){
matrix c;
c.init(a.n,b.m);
fo(i,1,c.n)
fo(j,1,c.m)
fo(k,1,a.m)
c.num[i][j]=(c.num[i][j]+a.num[i][k]*b.num[k][j])%MOD;
return c;
}
知道了这些就可以做题了.
题目大意:
一条长度为n的道路,每一次可以向前走1或k,询问走到n一共有多少方案.
数据范围:
第一行是闪烁技能的等级k(1<=k<=10)
第二行是监狱的个数n(1<=n<=2^31-1)
分析:
我们可以很快速的求出状态转移方程:
fo(i,1,n)
fo(j,1,k-1)
f[i]=(f[i]+f[j]);
我们发现由于n的值实在太大了,毫无疑问会超时,而k<=10,考虑k.根据矩阵乘法的性质,我们很容易可以得出,这是一道矩阵乘法可以解出的题目;
所以我们需要构造一个矩阵,使它与答案相乘等于下一次.
这里有两种构造方法,
若ans[1][k]
unit.init(k,k);
fo(i,1,k-1) unit.num[i][i+1]=1;
fo(i,1,k) unit.num[k][i]=1;
若ans[k][1] 反过来就好了
最后,记得单位矩阵(蒟蒻楼主刚刚知道),即一个矩阵p,与另一个任意矩阵q,p*q=q;
对角线全为1就好
最后附上code:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<queue>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int MOD=7777777,MAXN=15;
typedef long long ll;
struct matrix{
ll n,m,num[MAXN][MAXN];
void init(int x,int y){
n=x;m=y;
memset(num,0,sizeof(num));
}
};
matrix operator *(matrix a,matrix b){
matrix c;
c.init(a.n,b.m);
fo(i,1,c.n)
fo(j,1,c.m)
fo(k,1,a.m)
c.num[i][j]=(c.num[i][j]+a.num[i][k]*b.num[k][j])%MOD;
return c;
}
int n,k;
matrix Solve(){
matrix unit,ans;
unit.init(k,k);
fo(i,1,k-1) unit.num[i+1][i]=1;
fo(i,1,k) unit.num[i][k]=1;
ans.init(k,k); //ans是单位矩阵
fo(i,1,k) ans.num[i][i]=1;
for(int tot=n-k+1;tot;tot>>=1)
{
if(tot&1) ans=ans*unit;
unit=unit*unit;
}
return ans;
}
matrix ans;
int main(){
scanf("%d%d",&k,&n);
ans.init(1,k);ans.num[1][1]=1;
fo(i,2,k)
fo(j,1,i-1)
ans.num[1][i]=(ans.num[1][i]+ans.num[1][j])%MOD;
ans=ans*Solve();
// fo(i,1,k) printf("%lld ",ans.num[1][i]);
printf("%lld",ans.num[1][k]);
return 0;
}