矩阵连乘问题:
给定n 个矩阵(A0,A1,....An-1) 其中 Ai 和 Ai+1 是可乘的, i=0, 1,... , n-2 .
求解计算这n 个矩阵的连乘积A0A1....An-1 。
由于矩阵连乘满足结合律,因此矩阵的乘法可以有多种不同的计算次序,每种计算次序对应不同的数乘次数。可以利用加括号的方式确定计算次序。如果矩阵连乘完全加了括号,则说明计算次序确定。
例如 三个矩阵{A0,A1,A2} 连乘,其中矩阵的维数 分别为 10*100 100*5 和 5*50
其中有两种计算次序:
/// 数乘次数 = 左矩阵行数 * 右矩阵列数 * 左矩阵列数
第一种:(A0*A1)*A2 ; 该种加括号的方式: 数乘次数 = 10 *5 *100 +10*50*5 =7500
第二种: A0*(A1*A2) , 该种加括号的方式: 数乘次数 = 100*50*5 +10*50*100 =75000
思想: 计算Ai*....Aj 的次数 = Ai*...*Ak 的次数+ Ak+1 * ...*Aj 的次数 + 左右矩阵相乘的数。
/// Pk 代表从k 处划分右矩阵的行数, pj 代表 右矩阵相乘后的列数, pi 代表 右矩阵的列数
对于一组矩阵:A1(30x35),A2(35x15),A3(15x5),A4(5x10),A5(10x20),A6(20x25) 个数N为6
那么p数组保存它们的行数和列数:p={30,35,15,5,10,20,25}共有N+1即7个元素///最后第7个元素即为最后一个矩阵的列数
p[0],p[1]代表第一个矩阵的行数和列数,p[1],p[2]代表第二个矩阵的行数和列数......p[5],p[6]代表第六个矩阵的行数和列数
具体思想: 初始化,每个矩阵链长度为1 的数乘次数为0 即(m[i][i]==0)
当计算链长大于1 时, 利用前面已经算出结果, 求解问题:
m[i][j] = m[i+1][j]+ p[i+1]*p[i]*p[j+1]; ///利用之前划分的结果
最后在对 i ~ j 的矩阵 划分, 看是否有比原结果更小的划分。
for(int r=i+1;r<j;r++){ /// 进一步求子列的最优解
int t=m[i][r] + m[r+1][j] + p[i]*p[r+1]*p[j+1];
if(t<m[i][j]){
s[i][j]=r;///记录划分点
m[i][j]=t;
}
完整代码:
#include<iostream>
#include<cstdio>
using namespace std;
const int N =7;
void MartrixChain(int p[],int n,int s[][N],int m[][N])
{
int i,j,k;
for(i=0;i<n;i++)///i 到 i 表示的是 A0 ~ An-1 的单个矩阵,独立的矩阵,所以计算次数为0
m[i][i]=0;
for(k=2;k<=n;k++)///代表相乘矩阵的个数为 K
for(i=0;i<n-k+1;i++){
j=i+k-1;///i 为子列的起点, j 为末端
m[i][j] = m[i+1][j]+ p[i+1]*p[i]*p[j+1]; ///利用之前划分的结果
s[i][j]=i;//记录划分点
for(int r=i+1;r<j;r++){ /// 进一步求子列的最优解
int t=m[i][r] + m[r+1][j] + p[i]*p[r+1]*p[j+1];
if(t<m[i][j]){
s[i][j]=r;///记录划分点
m[i][j]=t;
}
}
}
}
void TraceBack(int s[][N],int i,int j)
{
if(i==j) return ;
cout<<"(";
TraceBack(s,i,s[i][j]);
TraceBack(s,s[i][j]+1,j);
cout<<")";
}
int main()
{
int R[]={30,35,15,5,10,20,25};
int m[N][N];
int s[N][N];
MartrixChain(R,6,s,m);
TraceBack(s,0,5);
return 0;
}