矩阵乘法优化DP
2017-11-08 13:44
381 查看
好久没更新博客了,更新一下吧,顺便也让自己熟悉一点。为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,所以可以改成这样:
然后,矩阵乘法只有结合律,没有交换律。即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 1000000007LL
struct 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。
先介绍一下矩阵:一个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 1000000007LL
struct 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。
相关文章推荐
- 【矩阵乘法优化DP】Codeforces 717D Dexterina’s Lab
- 【bzoj2476】战场的数目 矩阵乘法优化dp
- bzoj 1898: [Zjoi2005]Swamp 沼泽鳄鱼 (矩阵乘法优化DP)
- Codevs 1305 Freda的道路(矩阵乘法 DP优化)
- 程序碎片- 矩阵乘法优化(dp,循环)
- Poj 3734 Blocks(DP,矩阵乘法优化)
- 1009: [HNOI2008]GT考试 矩阵乘法优化DP+KMP
- 关于矩阵乘法优化dp(入门+斐波那契模板题)
- BZOJ 1875 [SDOI 2009] HH去散步 (DP,矩阵乘法优化)
- bzoj 4870: [Shoi2017]组合数问题 [矩阵乘法优化dp]
- [矩阵乘法优化DP] Topcoder SRM554. TheBrickTowerHardDivOne
- [BZOJ 1009] [HNOI2008] GT考试 【AC自动机 + 矩阵乘法优化DP】
- BestCoder Round #68 (div.1) B 矩阵乘法优化DP
- 矩阵乘法优化DP
- 【矩阵乘法优化DP】BZOJ1875 [SDOI2009]HH去散步
- [BZOJ]1875: [SDOI2009]HH去散步 矩阵乘法优化DP
- BZOJ 3120 Line【矩阵乘法优化dp
- 程序碎片- 矩阵乘法优化(dp,递归)
- BZOJ1009 GT考试 (DP 矩阵乘法优化)
- 【Contra】 矩阵乘法优化 dp