问题:给定n个矩阵:A1,A2,...,An,其中Ai与Ai+1是可乘的,i=1,2...,n-1。确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。输入数据为矩阵个数和每个矩阵规模,输出结果为计算矩阵连乘积的计算次序和最少数乘次数。
分析:由于矩阵乘法满足结合律,故计算矩阵的连乘积可以有许多不同的计算次序。这种计算次序可以用加括号的方式来确定。若一个矩阵连乘积的计算次序完全确定,也就是说该连乘积已完全加括号,则可以依此次序反复调用2个矩阵相乘的标准算法计算出矩阵连乘积。
完全加括号的矩阵连乘积可递归地定义为:
(1)单个矩阵是完全加括号的;
(2)矩阵连乘积A是完全加括号的,则A可表示为2个完全加括号的矩阵连乘积B和C的乘积并加括号,即A=(BC)
例如,矩阵连乘积A1A2A3A4有5种不同的完全加括号的方式:(A1(A2(A3A4))),(A1((A2A3)A4)),((A1A2)(A3A4)),((A1(A2A3))A4),(((A1A2)A3)A4)。每一种完全加括号的方式对应于一个矩阵连乘积的计算次序,这决定着作乘积所需要的计算量。
Ai....AkAk+1...Aj一组矩阵,ai-1*ak,ak*aj,的递推公式为
0;i=j
f(i,j)=
min(k){f(i,k)+f(k+1,j)+ai-1*ak*aj};i<j,k>=i,k<=j
递归法:
int a[m+1]={30,35,15,5,10,20,25};
int b[m+1][m+1];
int f(int i,int j){
int minvalue;
int k,temp;
if(i==j)
return 0;
minvalue=f(i+1,j)+a[i-1]*a[i]*a[j];
b[i][j]=i;
for(k=i+1;k<j;k++){
temp=f(i,k)+f(k+1,j)+a[i-1]*a[k]*a[j];
if(minvalue>temp){
minvalue=temp;
b[i][j]=k;
}
}
return minvalue;
}
void traceback(int i,int j){
if(i==j)
return ;
traceback(i,b[i][j]);
traceback(b[i][j]+1,j);
printf("A[%d-%d]*A[%d-%d]\n",i,b[i][j],b[i][j]+1,j);
}
int main(){
printf("%d\n",f(1,m));
traceback(1,m);
return 0;
}
动态规划法:
int a[m+1]={30,35,15,5,10,20,25};
int b[m+1][m+1];
int number[m+1][m+1]={0};
void traceback(int i,int j){
if(i==j)
return ;
traceback(i,b[i][j]);
traceback(b[i][j]+1,j);
printf("A[%d-%d]*A[%d-%d]\n",i,b[i][j],b[i][j]+1,j);
}
int main(){
int r;//参与矩阵运算次数
int i,j,k,temp;
for(r=2;r<m+1;r++){
for(i=1;i<=m-r+1;i++){
j=i+r-1;
b[i][j]=i;
number[i][j]=number[i+1][j]+a[i-1]*a[i]*a[j];
for(k=i+1;k<j;k++){
temp=number[i][k]+number[k+1][j]+a[i-1]*a[k]*a[j];
if(number[i][j]>temp){
number[i][j]=temp;
b[i][j]=k;
}
}
}
}
printf("%d\n",number[1][m]);
traceback(1,m);
return 0;
}
备忘录法:
int a[m+1]={30,35,15,5,10,20,25};
int b[m+1][m+1];
int number[m+1][m+1];
int f(int i,int j){
int minvalue;
int k,temp;
if(i==j)
return 0;
if(number[i][j]>0)
return number[i][j];
minvalue=f(i+1,j)+a[i-1]*a[i]*a[j];
b[i][j]=i;
for(k=i+1;k<j;k++){
temp=f(i,k)+f(k+1,j)+a[i-1]*a[k]*a[j];
if(minvalue>temp){
minvalue=temp;
b[i][j]=k;
}
}
number[i][j]=minvalue;
return minvalue;
}
void traceback(int i,int j){
if(i==j)
return ;
traceback(i,b[i][j]);
traceback(b[i][j]+1,j);
printf("A[%d-%d]*A[%d-%d]\n",i,b[i][j],b[i][j]+1,j);
}
int main(){
printf("%d\n",f(1,m));
traceback(1,m);
return 0;
}