矩阵乘法优化DP

来源:互联网 发布:模拟关注软件app 编辑:程序博客网 时间:2024/04/29 23:49

好久没更新博客了,更新一下吧,顺便也让自己熟悉一点。为noip提高攒攒rp。

先介绍一下矩阵:一个x*y的纵横二位数据表格。例如一个2*2的矩阵A可以是这样的:{(1,2)(3,4)}

那么在其中A(0,1)就是2,以此类推。。。

介绍完了最基本的矩阵,就来讲讲矩阵乘法。矩阵乘法就是两个矩阵相乘。但是其中要有限制:例如矩阵A*B。其中A的大小如果是a*b,那么B的大小就必须是b*c。

为什么呢?这就要讲到乘法的过程了。A*B=C的话,C(i,j)=所有A(i,k)*B(k,j)的和。可以看出A的列数和B的行数都等于k,故A的列数要等于B的行数。

还有A(a*b)*B(b*c)=C(a*c)这从上面的例子中也能很容易的看出来。

矩阵乘法转化成代码就很容易实现了。如下(下面是当A(n*t),B(t*m)的情况):

for(int i=0;i<n;i++)  for(int j=0;j<m;j++)    for(int k=0;k<t;k++)      c[i][j] += a[i][k]*b[k][j];
这样一次矩阵乘法的时间复杂度就是O(n^3)。可以有一点小优化,因为如果在a[i][k]=0时,那么就肯定不用算了,加的结果都是0,所以可以改成这样:
for(int i=0;i<n;i++)       for(int k=0;k<m;k++)if(a[i][k])  //优化           for(int j=0;j<t;j++)               c[i][j] +=a[i][k]*b[k][j]

然后,矩阵乘法只有结合律,没有交换律。即A*B*C=A*(B*C),但是A*B!=B*A。根据乘法过程想一下就知道是为什么了。

因为有结合律,所以说矩阵可以快速幂。就可以解决很多题目了。

例题:洛谷1962 传送门:https://www.luogu.org/problemnew/show/P1962。

题目大意:求斐波那契数列的第n项%1000000007的值。n在longlong范围内。

根据斐波那契数列,可以得出dp方程:f[i]=f[i-1]+f[i-2],n那么大,怎么做呢?这时我们发现,对于每一个f[i],他的结果都是f[i-1]+f[i-2],这时候就能用矩阵乘法了。

我们用矩阵A(1*2)来表示序列的第i项和第i+1项,用C(1*2)来表示序列的第i+1项和第i+2项。

那么根据矩阵乘法A*B(2*2)=C。我们就要把B求出来。

首先C(0,0) = A(0,0)*B(0,0)+A(0,1)*B(1,0),我们知道C(0,1)是序列的第i+1项,A(0,1)也是序列的第i+1项,所以说B(1,0)=1,B(0,0)=0。

然后C(0,1) = A(0,0)*B(0,1)+A(0,1)*B(1,1),C(0,1)是序列的第i+2项,根据dp方程,C(0,1)=A(0,0)+A(0,1)。所以B(0,1)=1,B(1,1)=1。

这样B就出来了,就是{(0,1)(1,1)}。对于初始的两项,乘n-1次B就能得到最终的n和n+1项。然而矩阵是有结合律的,中间可以用快速幂来乘,就不会超时了。

这样分析,代码就出来了:

#include<bits/stdc++.h>using namespace std;#define mod 1000000007LLstruct matrix{  //定义矩阵    long long a[2][2];    matrix(){        for(int i=0;i<2;i++)          for(int j=0;j<2;j++) a[i][j] = 0LL;    }}t,d;matrix operator * (matrix a,matrix b)  //为了程序的整洁,用重载运算符来写矩阵乘法。{    matrix c;//这里是没有速度的优化的。    for(int i=0;i<2;i++)      for(int j=0;j<2;j++)        for(int k=0;k<2;k++)          c.a[i][j] =(c.a[i][j]+a.a[i][k]*b.a[k][j]%mod)%mod;    return c;}matrix qpow(matrix now,long long x)  //快速幂。{    if(x==1LL) return now;    matrix c = qpow(now , x>>1);    if(x&1)return c*c*now;    else return c*c;}int main(){    long long n;    matrix ans;    scanf("%lld",&n);    if(n==1)    {        printf("1");        return 0;    }    t.a[0][0]=t.a[0][1]=d.a[0][1]=d.a[1][1]=d.a[1][0]=1LL;  //t为初始矩阵,d则为上述中的B。    ans = t * qpow(d,n-1LL);    printf("%d",ans.a[0][0]%mod);    return 0;}
这样的斐波那契数列是矩阵乘法优化DP中非常基础的。但只要找出DP的方程,如果有对于每个f[i]的答案都是一样的这个特点的话,经过详细分析,大部分都能转化为这样的矩阵乘法优化DP。

原创粉丝点击