矩阵连乘也是常见的动态规划例子之一。形式如下:给出N个矩阵,矩阵是有序的,即相邻矩阵之间可以进行乘法运算,求这些矩阵最小的相乘次数。如:矩阵30*15、15*5、5*10,先让前两个矩阵进行相乘时、再和最后一个矩阵相乘时,乘法计算次数为30*15*5+30*5*10=3750,;先让后两个矩阵相乘、再和第一个矩阵相乘时,乘法计算次数为15*5*10+30*15*10=5250。由此可见,不同的运算顺序,会影响到乘法的计算次数。
首先假设以下变量:
n表示矩阵的个数
Ai表示第i个矩阵
A[i:j]表示矩阵连乘AiA{i+1}..Aj
pi表示Ai的列数
p{i-1}表示Ai的行数
k表示矩阵连乘断开的位置为k,表示在Ak和A{k+1}之间断开
m[i,j]表示A[i:j]的最少乘次,m[1,n]即问题的最优解
假设k是对矩阵链A[i:j]一分为二得到最优解时的断开位置,则m[i,k]和m[k+1,j]分别是两个子矩阵链A[i,k]和A[k+1,j]的最优解。两个矩阵最后要相乘才能得到A[i,j],因此,最后要加上p{i-1}*pk*pj,也就是两子矩阵相乘的数乘次数,才能得到总的数乘次数。公式如下:
Java代码如下:
int arr[ ] = {30,15,5,10};
int minMultiplication = 0;
final int LENGTH = arr.length;
int[ ][ ] s = new int [ LENGTH ][ LENGTH ];
int[ ][ ] m = new int [ LENGTH ][ LENGTH ];
for(int i=0;i<LENGTH;i++)
{
for(int j=0;j<LENGTH;j++){
s[ i ][ j ] = 0;
m[ i ][ j ] = 0;
}
}
for(int r=2; r<=LENGTH-1; r++) //r为当前计算的链长(子问题规模)
{
for(int i=1; i<=LENGTH-1-r+1; i++)//LENGTH-1-r+1为最后一个r链的前边界
{
int j = i+r-1;//计算前边界为r,链长为r的链的后边界
m[ i ][ j ] = m[ i+1 ][ j ] + arr[ i-1 ]*arr[ i ]*arr[ j ];//将链ij划分为A(i) * ( A[i+1:j] )
s[ i ][ j ] = i; //s[ ][ ]存储各子问题的决策
for(int k=i+1; k<j; k++) //将链ij划分为( A[i:k] )* (A[k+1:j])
{
int tmp = m[ i ][ k ] + m[ k+1 ][ j ] + arr[ i-1 ]*arr[ k ]*arr[ j ];
if(tmp<m[ i ][ j ])
{
m[ i ][ j ] = tmp;
s[ i ][ j ] = k;
}
}
}
}
minMultiplication = m[1][LENGTH-1];
System.out.println(minMultiplication);