动态规划
矩阵连乘问题
问题描述:
- 给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,用加括号的方法表示矩阵连乘的次序,不同加括号的方法所对应的计算次序是不同的,求矩阵连乘的最佳计算次序
说明:
- 1.矩阵A和矩阵B可乘的条件:
矩阵A的列数=矩阵B的行数- 2.矩阵相乘过程中仅涉及加法和乘法,乘法耗时远远大于加法耗时,故采用矩阵连乘所需的乘法次数来代表不同计算次序的计算量
- 3.设矩阵A是p×q的矩阵,B是q×r的矩阵,乘积的结果C是p×r的矩阵,计算量是p * q * r
最优子结构性质分析:
- 1.将矩阵连乘的积AiAi+1…Aj简记为A[i][j] (i<=j)
- 2.考察计算A[1][n]的最优计算次序
设这个计算次序在矩阵AK和AK+1之间将矩阵链断开(1<=k<=n)- 3.计算量: A[i][k]的计算量+A[k+1][n]的计算量+A[1][k]和A[k+1][n]相乘的计算量
- 4.最优子结构性质: 计算A[1][n]的最优连乘次序所包含的计算矩阵子链A[i][k]和A[k+1][n]的连乘次序也是最优的
建立递归关系:
- 设计算A[i][j] (i<=j)所需的最少乘法次数为m[i][j]
- 1.原问题的最优值为m[1][n]
- 2.当i=j时,A[i][j] = A[i],只有一个矩阵不用相乘,所以m[i][i] = 0
- 3.当i<j时,m[i][j] =min{ m[i][k] + m[k+1][j] + piqkqj}
- 4.将n个矩阵的行数和列数存储在一位数组P[n](因为q m= p m+1,即第m个矩阵的列数等于第m+1个矩阵的行数 ,因此P中只需要存储n+1个元素,下标是从0 ~ n)
算法设计: 自底向上
- 步骤1:确定合适的数据结构
1.采用二维数组m来存放各个子问题的最优值
2.二维数组s来存放各个子问题的最优决策(如果s[i][j] = k,则最优加括号方法为(Ai…Ak)(Ak+1…Aj))
3.采用一位数组P来存放每个矩阵的的行数- 步骤2:初始化
1.令m[i][i] = 0,s[i][i] = 0(i = 1,2,3,…,n)- 步骤3:循环阶段
步骤3-1:
按照递归关系系计算两个矩阵AiAi+1 相乘时的最优解并将其存入m[i][i+1]中,同时将最优决策计入s[i][i+1] (i = 1,2,…,n-1)
步骤3-2:
按照递归关系式计算3个矩阵AiAi+1Ai+2 相乘时的最优解并将其存入m[i][i+2],同时将最优决策记入s[i][i+2](i = 1,2,…,n-2)
以此类推,直到
步骤3-(n-1):
按照递归关系式计算n个矩阵A1A2…An 相乘时的最优值并将其存入m[1][n],同时将最优决策记入s[1][n]
至此: m[1][n]即为原问题的最优值- 步骤4: 根据二维数组s记录的最优决策信息来构造最优解
步骤4-1:
递归构造A1…As[1][n]的最优解,直到只包含一个矩阵结束
步骤4-2:
递归构造As[1][n]+1…An的最优解,直到包含一个矩阵结束
**步骤4-3:**3
将步骤4-1和4-2递归的结果加括号
例题:
c++代码:
void MatriChain(){
for(int i=1; i<=n; i++){
//初始化,对角线上的计算量和加括号的位置为0,因为只有一个矩阵
m[i][i] = 0;
s[i][i] = 0;
}
//循环,从计算两个矩阵开始依次往下类推
for(int r=2; r<=n; r++){
for(int i=1; i<n-r+1;i++){
int j = i+r-1;
//决策为k=i的乘法次数
m[i][j] = m[i+1][j] + p[i-1]*p[i]*p[j];
s[i][j] = i;
//对A~i~...A~j~的所有决策,求最优值,记录最优决策
for(int k=i+1; k<j; k++){
int t = m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j];
if(t<m[i][j]){
m[i][j] = t;
s[i][j] = k;
}
}
}
}
}
void Traceback(int i,int j,int **s){
//s[i][j]记录了断开的位置
if(i==j){
return;
}
//递归打印A[i][s[i][j]]的加括号方式
Trackback(i,s[i][j],s);
//递归打印A[s[i][j]+1,[j]]的加括号方式
Trackback(s[i][j]+1,j,s);
cout<<"A"<<"["<<i<<":"<<s[i][j]<<"]"<<"乘以"<<"A""["<<(s[i][j]+1)<<":"<<j<<"]"<<endl;
}
算法分析: