题目: 确定n个矩阵连乘积 A1A2A3…An 的计算次序,使得按照这一次序计算矩阵连乘积,需要的"数乘"次数最小。
- 这个问题满足动态规划的条件
- 矩阵乘法满足结合律
- 两个矩阵相乘,要满足左边矩阵的列数 = 右边矩阵的行数
- 两个矩阵相乘,计算量为:左边矩阵的行数 乘以 左边矩阵的列数(即:右边矩阵的行数)乘以 右边矩阵的列数
- 问题实际上不是执行乘法,而只是决定所涉及的矩阵乘法的顺序
思路:
将矩阵连乘积记为 { P(i-1), Pi, P(i+1), … , Pk, … , Pj } :
- P(i-1) 是矩阵Ai的行数,
- Pi 是矩阵Ai的列数,也是矩阵A(i+1)的行数
- P(i+1) 是矩阵矩阵A(i+1)的列数
- 即:{P(i-1), Pi, P(i+1)}表示的是两个矩阵AiA(i+1)相乘
假设 矩阵连乘式A[i:j] 的最优计算次序的最后一次相乘在矩阵Ak和矩阵A(k+1)之间断开,
k的位置只有j-1种可能
计算量为:A[i:k]的最优计算量 + A[k+1:j]的最优计算量 + 最后乘法运算留下的两个矩阵相乘的计算量(如下图)
递归算法(使用备忘录:解决重叠子问题,提高算法效率):
#include<iostream>
using namespace std;
#define MaxSize 20
void MatrixChain(int *p, int n, int m[][MaxSize], int s[][MaxSize]){ //算最优乘法次数
int i,j,r,k;
int t; //t为中间量
for(i = 1; i <= n; i++){
m[i][i]= 0; //将单一矩阵都置为0,因为会在如上图中的算式出现,所以是置为0
}
for(r = 2; r <= n; r++){ //r表示连乘矩阵中矩阵的个数
for(i = 1; i <= n-r+1; i++){ //i表示连乘矩阵的第一个
j = i + r - 1; //j表示连乘矩阵的最后一个
m[i][j] = m[i+1][j] + p[i-1] * p[i] * p[j]; //此处i赋给k(参考上图中算式)
s[i][j] = i;
for(k = i+1; k < j; k++){
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;
}
}
}
}
cout<<endl<<"最优乘法次数:"<<m[1][n]<<endl;
}
void TraceBack(int i,int j, int s[][MaxSize]){ //最优乘法路径
if(i < j-1){
TraceBack(i,s[i][j],s);
TraceBack(s[i][j]+1,j,s);
cout<<"Matrix A"<<i<<" , "<<s[i][j]<<"and A"<<s[i][j]+1<<" , "<<j<<endl;
}
}
int main(){
int m[MaxSize][MaxSize], //m[i][j]记录从i到j所需最少连乘数
s[MaxSize][MaxSize]; //s[i][j]对应于m[i][j]断开位置
int p[MaxSize]; //存放矩阵的行数,列数
int n;
do{
cout<<"请输入矩阵连乘的矩阵个数(2到20)n = ";
cin>>n;
}while(n < 2 || n > 20);
cout<<"请依次输入n个矩阵的行数及最后一个矩阵的列数(输入n+1个数):" <<endl;
for(int i = 0; i < n+1; i++){
cin>>p[i];
}
MatrixChain(p,n,m,s);
TraceBack(1,n,s);
}