矩阵乘法满足结合律,因此可以选择不同的乘法顺序来进行。矩阵乘法进行的次数等于两个子序列各自乘法次数加上两个子序列做一次乘法所需次数。
M为乘法次数,c[i]等于第i个矩阵的列数。c[0]为第一个矩阵的行数,c[i-1]为第i个矩阵的行数
由于递归在一些情况下会重复计算,导致效率急剧降低,因此需要将递归变为非递归并且用表记录计算结果。
在这里我们需要找出最小乘法次数以及所对应的乘法顺序。
矩阵分别为A=50×10,B=10×40,C=40×30,D=30×5,计算ABCD。
#include <iostream>
#include <vector>
#include <limits>
using namespace std;
//一个简单的矩阵类
template <typename Object>
class Matrix
{
public:
Matrix(int row,int col):arr(row+1)
{
for(auto & x:arr)
{
x.resize(col+1);
}
}
vector<Object> & operator [](int row)
{
return arr[row];
}
const vector<Object> & operator [](int row)const
{
return arr[row];
}
private:
vector<vector<Object>> arr;
};
//计算矩阵运算的最少乘法次数,存储在m中,最终结果存储在m[1][n]。并记录乘法顺序,记录在lastChange中
//c[0]为第一个矩阵行数,c[i]为第i个矩阵的列数。第i个矩阵的行数等于c[i-1]
void optMatrix(const vector<int> & c,Matrix<int> & m,Matrix<int> & lastChange)
{
int n=c.size()-1; //矩阵个数
for(int left=1;left<=n;++left)
{
m[left][left]=0; //子序列只有一个矩阵,乘法次数为0
}
for(int k=1;k<n;++k) //子序列长度k+1
{
for(int left=1;left<=n-k;++left)
{
int right=left+k;
m[left][right]=INT_MAX;
//遍历子序列的所有可能,找出最小乘法次数
for(int i=left;i<right;++i)
{
int thisCost=m[left][i]+m[i+1][right]
+c[left-1]*c[i]*c[right];
if(thisCost<m[left][right])
{
m[left][right]=thisCost;
lastChange[left][right]=i;
}
}
}
}
}
void printOrdering(int left,int right,const Matrix<int> & lastChange)
{
if(left<right)
{
int i=lastChange[left][right];
cout<<i<<endl;
printOrdering(left, i, lastChange);
printOrdering(i+1, right, lastChange);
}
}
int main()
{
vector<int> c={50,10,40,30,5};
int n=c.size()-1;
Matrix<int> m(n,n);
Matrix<int> lastChange(n,n);
optMatrix(c, m, lastChange);
cout<<m[1][n];
cout<<endl;
printOrdering(1, n, lastChange);
}
结果
10500
1
2
3