一. 题目
输入: n+1个数字 p 0 , p 1 , . . . p n p_0,p_1,...p_n p0,p1,...pn表示矩阵 A 1 , A 2 , . . . , A n A_1,A_2,...,A_n A1,A2,...,An, 其中 A i : p i − 1 ∗ p i A_i:p_{i-1}*p_i Ai:pi−1∗pi ;
输出: 最小的连乘次序, 以及分割方法;
二. 思路分析
将矩阵链 A i A i + 1 . . . A j A_iA_{i+1}...A_j AiAi+1...Aj, 记为 A [ i : j ] A[i:j] A[i:j]
-
设二维数组 m [ i ] [ j ] m[i][j] m[i][j]为矩阵链 A i A i + 1 . . . A j A_iA_{i+1}...A_j AiAi+1...Aj的最优连乘次数;
-
设二维数组 s [ i ] [ j ] s[i][j] s[i][j],表示 A i A i + 1 . . . A j A_iA_{i+1}...A_j AiAi+1...Aj的最优分割位置.
建立的递归关系就是
计算A[i:j]所需的最小乘法次数为m(i,j)
利用表格进行理解: 其中行为i, 列为j
以输入为5,10,3,12,5,50,6为例
对角线上的元素m[i][i],表示从第i个矩阵乘到第i个矩阵需要的最小乘法计算次数, 这当然是0啦.
从一个长度r=2的矩阵链开始查找所有长度为r的矩阵链的最优乘法次序, r不断增大直到等于原链长度, 即可得到问题的答案.
从表格上看, r=2的部分就是从(1,2)开始的一斜列, 此时k就只有一种情况(k=1); 当r=3时, 所有长度为r的矩阵链对应于表格上(1, 3)开始的一斜列, 对于(1,3)格, 我们需要遍历的是k=1和k=2的情况, 即(1,3)格前面两格的情况, 此规律可以推广到任意一格的计算, 最终的结果在r=6时出现, 即表格上的(1,6)格.
最终最小的乘法次数, 以及分割位置表格如下
最终的分割形式: ( A 1 A 2 ) ( ( A 3 A 4 ) ( A 5 A 6 ) ) (A_1A_2)((A_3A_4)(A_5A_6)) (A1A2)((A3A4)(A5A6))
三.代码实现
代码如下: (摘录自https://blog.csdn.net/qq_44755403/article/details/105015330)
#include<iostream>
using namespace std;
const int N = 100;
int A[N];//矩阵规模
int m[N][N];//最优解
int s[N][N];//分割位置
void MatrixChain(int n)
{
int r, i, j, k;
for (i = 0; i <= n; i++)//初始化对角线
{
m[i][i] = 0;
}
for (r = 2; r <= n; r++)//r个矩阵连乘
{
for (i = 1; i <= n - r + 1; i++)//r个矩阵的r-1个空隙中依次测试最优点
{//遍历所有长度为r的子矩阵链
j = i + r - 1;
m[i][j] = m[i][i]+m[i + 1][j] + A[i - 1] * A[i] * A[j];
s[i][j] = i;
for (k = i + 1; k < j; k++)//变换分隔位置,逐一测试
{
int t = m[i][k] + m[k + 1][j] + A[i - 1] * A[k] * A[j];
if (t < m[i][j])//如果变换后的位置更优,则替换原来的分隔方法。
{
m[i][j] = t;
s[i][j] = k;
}
}
}
}
}
void print(int i, int j)
{
if (i == j)
{
cout << "A[" << i << "]";
return;
}
cout << "(";
print(i, s[i][j]);
print(s[i][j] + 1, j);//递归1到s[1][j]
cout << ")";
}
int main()
{
int n;//n个矩阵
cin >> n;
int i, j;
for (i = 0; i <= n; i++)
{
cin >> A[i];
}
MatrixChain(n);
cout << "最佳添加括号的方式为:";
print(1, n);
cout << "\n最小计算量的值为:" << m[1][n] << endl;
return 0;
}