给定n个矩阵构成的一个链
注意:在矩阵链乘问题中,实际上并没有把矩阵相乘,目的是确定一个具有最小代价的矩阵相乘顺序。找出这样一个结合顺序使得相乘的代价最低。
- 最优加全部括号的结构
动态规划第一步是寻找一个最优的子结构。假设现在要计算AiAi+1…Aj的值,计算Ai…j过程当中肯定会存在某个k值(i<=k有分析可以到最优子结构为:假设AiAi+1…Aj的一个最优加全括号把乘积在Ak和Ak+1之间分开,则Ai…k和Ak+1…j也都是最优加全括号的。 - 一个递归解
设m[i,j]为计算机矩阵Ai…j所需的标量乘法运算次数的最小值,对此计算A1…n的最小代价就是m[1,n]。现在需要来递归定义m[i,j],分两种情况进行讨论如下:
当i==j时:m[i,j] = 0,(此时只包含一个矩阵)
当i- 计算最优代价
虽然给出了递归解的过程,但是在实现的时候不采用递归实现,而是借助辅助空间,使用自底向上的表格进行实现。设矩阵Ai的维数为pi-1pi,i=1,2…n。输入序列为:p=,length[p] = n+1。使用m[n][n]保存m[i,j]的代价,s[n][n]保存计算m[i,j]时取得最优代价处k的值,最后可以用s中的记录构造一个最优解。书中给出了计算过程的伪代码,摘录如下: - 构造一个最优解
第三步中已经计算出来最小代价,并保存了相关的记录信息。因此只需对s表格进行递归调用展开既可以得到一个最优解。
算法程序分为两部分,计算部分和递归输出部分
假设需要计算 i 个矩阵 相乘,则任意两个矩阵之间都需要计算一次代价(自己到自己也需要计算,虽然一定为0),共需要1+2+3+…+i 次,存储到m矩阵(代价矩阵)是一个1到 i的阶梯型,举例当i=5时,m矩阵为
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
1 | * | ||||
2 | * | * | |||
3 | * | * | * | ||
4 | * | * | * | * | |
5 | * | * | * | * | * |
再来看s矩阵,s矩阵存储的是 i 矩阵乘到 j 矩阵时最优的分割处 k的取值,与m矩阵不同的是,任意两个矩阵之间都需要计算 k值但是自己到自己不需要计算,共需要1+2+3+…+(i-1)次,存储到s矩阵(k值矩阵)是一个1到 i-1的阶梯型,举例当i=5时,s矩阵为
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
1 | |||||
2 | * | ||||
3 | * | * | |||
4 | * | * | * | ||
5 | * | * | * | * |
可以发现s矩阵和m矩阵刚好可以放在同一个 i*i 的矩阵中,这样我们编程的时候就可以只定义一个 i*i 数组,将s矩阵存放在m矩阵对角的位置,循环变量我们准备使用一个矩阵位置 j 和要计算的另一个矩阵到 j 矩阵的距离 n ,减少资源的占用
预编译部分,定义全局变量
#define Matrix_MUM 8
int min_s,min_k,M[Matrix_MUM][Matrix_MUM]={0},p[Matrix_MUM+1]={4,5,3,6,4,5,3,6,4};
main()
函数,n 是 i 到 j 相差的矩阵数,用到了 j 和 n 两个变量,i 可以通过 j 和 n 计算出来
int main()
{
int j,n;
for(n=1;n<Matrix_MUM;n++)
for(j=n;j<Matrix_MUM;j++)
chain(j,j-n),M[j-n][j]=min_s,M[j][j-n]=min_k;
}
chain()
函数,计算k值
void chain(int j,int i)//计算
{
int s,k;
for(k=i;k<j;k++)
{
s=M[i][k]+M[k+1][j]+p[i]*p[k+1]*p[j+1];
if(k==i)min_s=s,min_k=k;
if(s<min_s)min_s=s,min_k=k;
}
}
*c()
函数,递归输出,当i和j相等或者相差1的时候结束,否则根据 k值分割继续递归
char *c(int i,int j)//结果
{
char s[80],t[80];
if(i==j)
{
s[0]='A';
s[1]='0'+i;
s[2]=0;
return &s[0];
}
if(j-i==1)
{
s[0]='A';
s[1]='0'+i;
s[2]=0;
t[0]='A';
t[1]='0'+j;
t[2]=0;
return kh(s,t);
}
strcpy(s,c(i,M[j][i]));
strcpy(t,c(M[j][i]+1,j));
return kh(s,t);
}
kh()
函数,给字符串前后加上括号
char *kh(char *str1,char *str2)//括号
{
char str[80]="(";
strcat(str,str1);
strcat(str,str2);
strcat(str,")");
return &str[0];
}
#include
#include
#include
using namespace std;
#define Matrix_MUM 8
int min_s,min_k,M[Matrix_MUM][Matrix_MUM]={0},p[Matrix_MUM+1]={4,5,3,6,4,5,3,6,4};
int main()
{
char s[80];
char *c(int,int);
void chain(int j,int i);
void printmatrix();
int j,n;//n:j-i ji之间相差几个矩阵
for(n=1;n<Matrix_MUM;n++)
for(j=n;j<Matrix_MUM;j++)//i=j-n
chain(j,j-n),M[j-n][j]=min_s,M[j][j-n]=min_k;//s矩阵存在对称位置
printmatrix();
strcpy(s,c(0,6));
puts(s);
getchar();
return 0;
}
void chain(int j,int i)//计算
{
int s,k;
for(k=i;k<j;k++)
{
s=M[i][k]+M[k+1][j]+p[i]*p[k+1]*p[j+1];
if(k==i)min_s=s,min_k=k;
if(s<min_s)min_s=s,min_k=k;
}
}
void printmatrix()//输出
{
int i,j;
cout<<"输出m矩阵"<<endl;
for(j=0;j<Matrix_MUM;j++)
{
for(i=0;i<=j;i++)
cout<<M[i][j]<<" ";
cout<<endl;
}
cout<<endl<<"输出s矩阵";
for(j=0;j<Matrix_MUM;j++)
{
for(i=0;i<=j-1;i++)
cout<<M[j][i]+1<<" ";
cout<<endl;
}
}
char *kh(char *str1,char *str2)//括号
{
char str[80]="(";
strcat(str,str1);
strcat(str,str2);
strcat(str,")");
return &str[0];
}
char *c(int i,int j)//结果
{
char s[80],t[80];
if(i==j)
{
s[0]='A';
s[1]='0'+i;
s[2]=0;
return &s[0];
}
if(j-i==1)
{
s[0]='A';
s[1]='0'+i;
s[2]=0;
t[0]='A';
t[1]='0'+j;
t[2]=0;
return kh(s,t);
}
strcpy(s,c(i,M[j][i]));
strcpy(t,c(M[j][i]+1,j));
return kh(s,t);
}