【问题描述】
给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2…,n-1。如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。例如,给定三个连乘矩阵{A1,A2,A3}的维数分别是10100,1005和550,采用(A1A2)A3,乘法次数为101005+10550=7500次,而采用A1(A2A3),乘法次数为100550+10100*50=75000次乘法,显然,最好的次序是(A1A2)A3,乘法次数为7500次。
a图 对角线为运算次数,方向为向着右上角运算,即运算到链长为总链长时的最小次数
c图 s(1,6)的值为3,意味着链长从1~6断点为3,s(1,3)断点为1,
再去查找从4~6,s(4,6)=5,断点为5,因此得到结果((A1(A2A3))((A4A5)A6))
【源代码】
递归备忘录法:(循环备忘录往下翻⬇)
#include <iostream>
using namespace std;
int MatrixChain(int i, int j, int **m, int **s, int *p)
{
if (m[i][j] > 0)
return m[i][j];
if (i == j)
return 0;
//m[i][j]表示在上一次的基础上,直接乘剩下矩阵时所得到的次数
int q = MatrixChain(i, i, m, s, p) + MatrixChain(i + 1, j, m, s, p) + p[i - 1] * p[i] * p[j];
//s矩阵记录此时的分断位点时i
s[i][j] = i;
for (int k = i + 1; k < j; k++)
{
//遍历从i+1开始一直到j的分段方法,找到一个最小的乘法次数时的分段方法,保留下来
int t = MatrixChain(i, k, m, s, p) + MatrixChain(k + 1, j, m, s, p) + p[i - 1] * p[k] * p[j];
if (t < q)
{
q = t;
//保存断点
s[i][j] = k;
}
}
//保存数值
m[i][j] = q;
return q;
}
void Trace(int i, int j, int **s)
{
if (i == j)
{
cout << "A" << i;
}
else
{
cout << "(";
Trace(i, s[i][j], s);
Trace(s[i][j] + 1, j, s);
cout << ")";
}
}
int main()
{
int size = 7;
// cout<<"input size:"<<endl;
// cin>>size;
int p[size] = {30, 35, 15, 5, 10, 20, 25};
//由于矩阵乘法规则,[]a,b *[]b,c =[]a,c,所以矩阵序列(30,35) (35,15)
int M[size][size], S[size][size];
int **m = new int *[size];
int **s = new int *[size];
//建立二维向量
for (int i = 0; i < size; i++)
{
m[i] = M[i];
s[i] = S[i];
}
//只有一个矩阵时,不需要乘,次数为0
for (int i = 1; i <= size; i++)
{
for (int j = 1; j <= size; j++)
m[i][j] = 0;
}
MatrixChain(1, size, m, s, p);
cout << "Min Times:" << m[1][size - 1] << endl;
//输出二维表
for (int i = 1; i < size; i++)
{
for (int j = i; j < size; j++)
{
cout << m[i][j] << " ";
}
cout << endl;
}
Trace(1, size - 1, s);
return 0;
}
循环备忘录法:
#include <iostream>
using namespace std;
void MatrixChain(int *p, int n, int **m, int **s)
{
//只有一个矩阵时,不需要乘,次数为0
for (int i = 1; i <= n; i++)
m[i][i] = 0;
for (int r = 2; r < n; r++)
{
//每循环一次,就会对对角线进行一次填充,直到循环到最右上角
//每次链长+1,所以元素-1
for (int i = 1; i <= n - r + 1; i++)
{
//i为链的起始段,j为链的末端
int j = i + r - 1;
//m[i][j]表示在上一次的基础上,直接乘下一个矩阵时所得到的次数
m[i][j] = m[i + 1][j] + p[i - 1] * p[i] * p[j];
//s矩阵记录此时的分断位点时i
s[i][j] = 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];
//遍历从i+1开始一直到j的分段方法,找到一个最小的乘法次数时的分段方法,保留下来
if (t < m[i][j])
{
m[i][j] = t;
s[i][j] = k;
}
}
}
}
}
void Trace(int i, int j, int **s)
{
if (i == j)
{
cout << "A" << i;
}
else
{
cout << "(";
Trace(i, s[i][j], s);
Trace(s[i][j] + 1, j, s);
cout << ")";
}
}
int main()
{
int size = 7;
// cout<<"input size:"<<endl;
// cin>>size;
int p[size] = {30, 35, 15, 5, 10, 20, 25};
//由于矩阵乘法规则,[]a,b *[]b,c =[]a,c,所以矩阵序列(30,35) (35,15)
int M[size][size], S[size][size];
int **m = new int *[size];
int **s = new int *[size];
//建立二维向量
for (int i = 0; i < size; i++)
{
m[i] = M[i];
s[i] = S[i];
}
MatrixChain(p, size, m, s);
cout << "Min Times:" << m[1][size - 1] << endl;
//输出二维表
for (int i = 1; i < size; i++)
{
for (int j = i; j < size; j++)
{
cout << m[i][j] << " ";
}
cout << endl;
}
Trace(1, size - 1, s);
return 0;
}