1. 问题
设S1,A2,A3,…An为n个矩阵的序列,其中Ai为Pi-1×Pi阶矩阵,这个矩阵链的输入用向量P=<P0,P1…Pn>给出。
给定向量P,确定一种乘法次序,使得基本运算的总次数达到最小。
2. 解析
我才用的矩阵为
A1(30×35)、
A2(35×15)、
A3(15×5)、
A4(5×10)、
A5(10×20)、
A6(20×25),
那么得到的P=<30,35,15,5,10,20,25>
设m[i,j]为计算机矩阵Ai…j所需的标量乘法运算次数的最小值,对此计算A1…n的最小代价就是m[1,n]。现在需要来递归定义m[i,j],分两种情况进行讨论如下:
1)当i==j时:m[i,j] = 0,(此时只包含一个矩阵)
2)当i<j 时:从步骤1中需要寻找一个k(i≤k<j)值,使得m[i,j] =min{m[i,k]+m[k+1,j]+pi-1pkpj} (i≤k<j)
设矩阵Ai的维数为pi-1pi,i=1,2…n。输入序列为:p=<p0,p1,…pn>,length[p] = n+1。使用m[n][n]保存m[i,j]的代价,s[n][n]保存计算m[i,j]时取得最优代价处k的值,最后可以用s中的记录构造一个最优解。
实现过程定义两个二维数组m和s,为了方便计算其第一行和第一列都忽略,行标和列标都是1开始。
3. 设计
#include <iostream>
using namespace std;
#define N 6
#define MAXVALUE 1000000
void matrix_chain_order(int* p, int len, int m[N + 1][N + 1], int s[N + 1][N + 1]);
void print_optimal_parents(int s[N + 1][N + 1], int i, int j);
int main()
{
int p[N + 1] = { 30,35,15,5,10,20,25 };
int m[N + 1][N + 1] = { 0 };
int s[N + 1][N + 1] = { 0 };
int i, j;
matrix_chain_order(p, N + 1, m, s);
cout << "最佳次序为:" << endl;
print_optimal_parents(s, 1, N);
cout << endl;
return 0;
}
void matrix_chain_order(int* p, int len, int m[N + 1][N + 1], int s[N + 1][N + 1])
{
int i, j, k, t;
for (i = 0; i <= N; ++i)
m[i][i] = 0;
for (t = 2; t <= N; t++) //当前链乘矩阵的长度
{
for (i = 1; i <= N - t + 1; i++) //从第一矩阵开始算起,计算长度为t的最少代价
{
j = i + t - 1;//长度为t时候的最后一个元素
m[i][j] = MAXVALUE; //初始化为最大代价
for (k = i; k <= j - 1; k++) //寻找最优的k值,使得分成两部分k在i与j-1之间
{
int temp = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j];
if (temp < m[i][j])
{
m[i][j] = temp; //记录下当前的最小代价
s[i][j] = k; //记录当前的括号位置,即矩阵的编号
}
}
}
}
}
//s中存放着括号当前的位置
void print_optimal_parents(int s[N + 1][N + 1], int i, int j)
{
if (i == j)
cout << "A" << i;
else
{
cout << "(";
print_optimal_parents(s, i, s[i][j]);
print_optimal_parents(s, s[i][j] + 1, j);
cout << ")";
}
}
4. 分析
matrix_chain_order函数具有循环嵌套,深度为3层,运行时间为O(n3)。
如果采用递归进行实现,则需要指数级时间Ω(2n),因为中间有些重复计算。