(1)矩阵乘法优化DP
矩阵乘法:
一个 a*b 的矩阵 M1 与一个 b*c 的矩阵 M2 相乘,得到一个 a*c 的矩阵 M3。
M3[i][j] = sigma(M1[i][k] * M2[k][j]) ( i <= a; j <= c; k <= b )
M3 的一个元素 M3[i][j] 是矩阵 M1 的第 i 行与 M2 的第 j 列的每一个对应元素乘积的和。
注意:矩阵 M1 的列数与矩阵 M2 的行数必须相同,否则无法相乘。(因此,只有行数与列数相等的矩阵才能有乘方。)
运算规律:矩阵乘法满足结合律,但不满足交换律。(交换后甚至有可能无法相乘。)
矩阵乘法优化线性递推式:
f[n] = f[n-1] * A + f[n-2] * B , 要求快速求出 f[n]
我们可以构造一个 1*2 的初始矩阵,开始时为 [ f[1] f[2] ] , 我们需要得到的下一个矩阵是 [ f[2] f[3] ] ,即为 [ f[2] (f[2] * A + f[1] * B) ]。
那么,我们应该构造一个 2*2 的“转移矩阵”,第一行 [ 0 B ] , 第二行 [ 1 A ] , 将初始矩阵与转移矩阵相乘就能得到 [ f[2] f[3] ] 。
将矩阵 [ f[2] f[3] ] 再与转移矩阵相乘,就能得到 [ f[3] f[4] ]。那么我们要快速的求出 f[n] ,就可以使用矩阵乘法的结合律。
为了求出 f[n] , 我们要求出 [初始矩阵] * ([转移矩阵]^(n-1)) 。那么我们可以先把后面转移矩阵的乘幂求出,再用初始矩阵与它相乘。
矩阵的乘幂同样可以使用快速幂在 O(logn) 的时间复杂度内求出。
矩阵乘法优化DP:
某一类DP的状态转移方程,也是一个线性递推式,f[i][] 只与 f[i-1][] 有关,比如下面这道例题:
有一个n个点m有向边的图,问:从1出发,经过L条边后恰好到达n有多少条路径?答案模p。 每条边可以被重复经过多次,并且被重复计数。N<=100;L<=10^15 。
我们使用状态 f[i][j] , 表示经过 i 条边到达 j 的路径条数,那么状态转移方程即为:
f[i][j] = sigma(Map[k][j] * f[i-1][k]) 其中 Map[k][j] 为从 k 到 j 的边数。
这个状态转移方程完全符合使用矩阵乘法优化的条件,因为 f[i][] 只与 f[i-1][] 有关,并且所有的 Map[k][j] 都是已知且固定不变的。
我们就使用一个f[0][] 数组作为初始矩阵,只有 f[0][1] 为 1, 所以初始矩阵为 [ 1 0 0 0 0 ..... 0 ] ,而转移矩阵就是 Map[][] 。这样就可以快速求出
f[L][] ,也就是要求的答案 f[L][n] 。
另一个更复杂一些的例题:BZOJ-1297 迷路:
每条边有一个边权w[i](1<=w[i]<=5),问:经过长度为L的路径后恰好到达n的路径数模p。 N<=20,m<=1000,L<=10^15 。
那么,首先来看数据范围,w[i] <= 5 , sth神犇教育我们 : 看到小与等于10的数据范围,首先想到什么? 拆点,状压,容斥。
如果我们使用拆点的方法,这道题就与上一题没有什么区别了。我们将每个点拆成 5 个点 i1,i2,i3,i4,i5 , 从 i1 到 i2,从 i2 到 i3 ,.... , 都有一条长度为一的边。如果从点 i 到点 j 有一条 长度为 3 的边,那么我们就从 i3 向 j1 连一条长度为一的边,这样就把长度不一的边全部转化为了长度为 1 的边。
刷题:
Bzoj1009 GT考试
Bzoj2165 大楼
Bzoj1875 HH去散步
Bzoj2004 公交线路