1. 采用标准的矩阵乘法来计算M1、M2和M3三个矩阵的乘积M1M2M3,设这三个矩阵的维数分别是2 × 10、10 × 2和2 × 10。如果先把M1和M2相乘,然后把结果和M3相乘,那么要进行2× 10 × 2 + 2 × 2 × 10 = 80次乘法;如果代之用M2和M3相乘的结果去乘M1,那么数量乘法的次数为10× 2 × 10 + 2 × 10 × 10 = 400。显然,执行乘法M1(M2M3)耗费的时间是执行乘法(M1M2)M3的5倍。一般来说,n个矩阵M1M2……Mn链乘法的耗费,取决于n– 1个乘法执行的顺序,请设计一个动态规划算法,使得计算矩阵链乘法时需要的数量乘法次数达到最小。
矩阵 | M1 | M2 | M3 | M4 | M5 | M6 |
维数 | 30 × 35 | 35 × 15 | 15 × 5 | 5 × 10 | 10 × 20 | 20 × 25 |
由于问题中存在大量的重叠子问题,使用分治算法递归得出结果的效率并不高,因此可以使用动态规划来解决。首先创建一个函数functionD(参数为p数组:用于存放矩阵的行列数,n:矩阵的规模,二维数组m用来存放指定范围矩阵相乘的次数最小值,二维数组s:用于存放指定范围矩阵的最佳断开位置),首先使用for循环将单个矩阵相乘的次数设为零,其次通过使用for循环来控制矩阵的规模,使其规模递增,在一个规模里将默认的断点设为第一个矩阵处断开,其次再使用for循环控制断点的移动,然后通过if语句来得出是否为最佳断点。当所有规模都循环结束时,得到的m[i][n]便是整个矩阵链的最短乘法次数。
#include <iostream>
using namespace std;
void functionD(int *p,int n,int **m,int **s) {
for (int i=1 ; i <= n; i++){
m[i][i]=0;//将单个矩阵相乘的次数设为0
}
for(int r=2 ; r <= n; r++) {//r为矩阵链的规模
for(int i=1; i<=n-r+1;i++){//i为前边界
int j=i+r-1;//j为后边界
m[i][j]=m[i][i]+m[i+1][j]+p[i-1]*p[i]*p[j];//记录断点在i处时所需要的乘法次数
s[i][j]=i;//将断点初始化在i处
for(int k=i+1;k<j;k++){
int t=m[i][k]/*实现重叠子问题的利用*/+m[k+1][j]+p[i-1]*p[k]*p[j];
if(t<m[i][j]) {//如果新的断点的乘法次数小于原来的次数
m[i][j]=t;//更新最少乘法次数
s[i][j]=k;//保存最佳断点位置
}
}
}
}
}
int main(){
int p[] = {30,35,15,5,10,20,25};//矩阵链的行列数
int n=6;//矩阵的个数
int **m,**s;
m=new int *[n];
for(int i=0;i<n;i++){
m[i]=new int [n];
}
s=new int *[n];
for(int i=0;i<n;i++){
s[i]=new int [n];
}
functionD(p,n,m,s) ;
cout<<"最少乘法次数为"<<m[1][n]<<endl;
return 0;
}