这类题目体现了DP的实质,也是经典问题。(•̀˓◞•́)
假设我们要用标准的矩阵乘法计算 M1 M 1 、 M2 M 2 、 M3 M 3 的乘积 M1M2M3 M 1 M 2 M 3 ,这三个矩阵的维数分别是2x10,10x2,2x10。
可见,矩阵链相乘时的顺序不同,运算量也不同。而我们的目的是找到一种乘法顺序使得运算量最小。
我们注意到,对于矩阵链 M1M2...Mi M 1 M 2 . . . M i ,矩阵 Mi M i 的列数一定等于矩阵 Mi+1 M i + 1 的行数( 1≤i<n 1 ≤ i < n ),这是由矩阵乘法的定义决定的。
因此,对于一个矩阵链,我们指定每个矩阵的行数和最右面矩阵 Mn M n 的列数就可以了。假设有n+1维数 r1,r2,...,rn+1 r 1 , r 2 , . . . , r n + 1 ,这里 ri r i 表示矩阵 Mi M i 的行数( 1≤i≤n 1 ≤ i ≤ n ), rn+1 r n + 1 表示最矩阵 Mn M n 的列数。
以后,我们用 Mi,j M i , j 来记 MiMi+1...Mj M i M i + 1 . . . M j 的乘积。用 C[i][j] C [ i ] [ j ] 来记录链 Mi,j M i , j 数量乘法的次数。
对于给定的一对索引 i i 和 j,1≤i<j≤n j , 1 ≤ i < j ≤ n , Mi,j M i , j 可用如下方法计算:
设 k k 是 i+1 i + 1 和 j j 之间的一个索引,索引 k k 把矩阵链 Mi,j M i , j 分成了两部分: Mi,k−1=MiMi+1...Mk−1 M i , k − 1 = M i M i + 1 . . . M k − 1 和 Mk,j=MkMk+1...Mj M k , j = M k M k + 1 . . . M j 。所以 Mi,j=Mi,k−1Mk,j M i , j = M i , k − 1 M k , j 。
用这种方法计算 Mi,j M i , j 的耗费(即数量乘法的次数),是计算 Mi,k−1 M i , k − 1 的耗费加上计算 Mk,j M k , j 的耗费再加上 Mi,k−1 M i , k − 1 乘以 Mk,j M k , j 的耗费(它是 rirkrj+1 r i r k r j + 1 )。
我们需要遍历 k k ,找到使乘法 MiMi+1...Mj M i M i + 1 . . . M j 所需的数量乘法最小的 k k 值,我们有以下递推式:
C[i][j]= C [ i ] [ j ] = Mini<k≤j M i n i < k ≤ j {C[i][k−1]+C[k][j]+rirkrj+1} { C [ i ] [ k − 1 ] + C [ k ] [ j ] + r i r k r j + 1 }
为了找出 M1M2..Mn M 1 M 2 . . M n 的乘法次数,我们只需要解递推式:
C[1][n]= C [ 1 ] [ n ] = Min1<k≤n M i n 1 < k ≤ n {C[1][k−1]+C[k][n]+rirkrn+1} { C [ 1 ] [ k − 1 ] + C [ k ] [ n ] + r i r k r n + 1 }
对角线 d d 用乘出各种 d+1 d + 1 个相继矩阵的最小耗费填满。特别地,对角线5恰好由一项组成,它表示6个矩阵相乘的最小耗费,这就是我们要求的结果。
我们从对角线0开始,到对角线5为止,沿着对角线填充这个三角形表。
下面我们给出算法的伪代码实现
MATCHAIN
输入
:n个矩阵的链的维数对应于正整数数组 r[1...n+1] r [ 1... n + 1 ] ,其中, r[1...n] r [ 1... n ] 是n个矩阵的行数, r[n+1] r [ n + 1 ] 是 Mn M n 的列数。
输出
:n个矩阵相乘的数量乘法的最小次数。
for i=1 to n {填充对角线d0}
C[i,i]=0;
end for
for d=1 to n-1 {填充对角线d1到dn-1}
for i=1 to n-d {填充对角线di的项目}
j=i+d
comment:下列三行计算C[i,j]
C[i,j]=inf
for k=i+1 to j
C[i,j]=Min{C[i,j],C[i,k-1]+C[k,j]+r[i]*r[k]*[j+1]
end for
end for
end for
return C[1,n]
对于某个常数c>0,算法的运行时间正比于:
显然,算法所需要的内存空间取决于所需要的三角数组的大小,也就是 Θ(n2) Θ ( n 2 ) 。