矩阵连乘问题
问题描述:
给定n个矩阵:A1,A2,…,An,其中Ai与Ai+1是可乘的,i=1,2…,n-1。确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。输入数据为矩阵个数和每个矩阵规模,输出结果为计算矩阵连乘积的计算次序和最少数乘次数。
问题分析:
1.分析最优解的结构
设计求解具体问题的动态规划算法的第一步是刻画该问题的最优解的结构特征。我们将矩阵连乘积AiAi+1…Aj简记为A[ i : j ]。考察计算A[ 1: n]的最优计算次序。设这个计算次序在矩阵Ak和Ak+1之间将矩阵链断开,1<=k<n,则其相应的完全加括号形式为((A1…Ak)(Ak+1…An))。以此次序,总的计算量为A[ 1 : k ]的计算量加上A[ k+1 : n ]的计算量, 再加上A[ 1 : k ]和A[ k+1 : n ]相称的计算量。
这个问题的关键特征是:计算A[ 1 :n ]的最优次序所包含的计算矩阵子链a[ 1 : k ]和A[ k+1 : n ]的次序也是最优的。因此,矩阵连乘积计算次序问题的最优解包含着其子问题的最优解。这种性质称为最优子结构性质。问题的最优子结构性质是该问题可以用动态规划算法求解的显著特征。
2.建立递归关系
从连乘矩阵个数为2开始计算每次的最小乘次数m[i][j]: m[0][1] m[1][2] m[2][3] m[3][4] m[4][5] //m[0][1]表示第一个矩阵与第二个矩阵的最小乘次数
然后再计算再依次计算连乘矩阵个数为3:m[0][2] m[1][3] m[2][4] m[3][5]
连乘矩阵个数为4:m[0][3] m[1][4] m[2][5]
连乘矩阵个数为5:m[0][4] m[1][5]
连乘矩阵个数为6:m[0][5] //即最后我们要的结果
m[i][j]给出了最优值,即计算A[i:j]所需的最少数乘次数。同时还确定了计算A[i:j]的最优次序中的断开位置k,也就是说,对于这个k有m[i][j]=m[i[k]+m[k+1][j] + Pi-1*Pk*Pj
若将对应于m[i][j]的断开位置k记为s[i][j],在计算最优值m[i][j]后,可以递归地有s[i][j]构造出相应的最优解。
-
计算最优值
根据计算m[ i ][ j ]的递归式,容易写一个递归算法计算m[ 1 ][ n ]。但是简单地递归将好费指数计算时间。在递归计算时,许多子问题被重复计算多次。这也是该问题可以用动态规划算法求解的又一显著特征。
用动态规划算法解决此问题,可依据其递归是以自底向上的方式进行计算。在计算的过程中,保存以解决的子问题答案。每个子问题只计算一次,而在后面需要时只要简单查一下,从而避免大量的重复计算。
#include<iostream>
#include<cstring>
using namespace std;
const int maxsize = 100;
int p[maxsize]{ 30,35,15,5,10,20,25 };
int m[maxsize][maxsize], s[maxsize][maxsize];//m[i][j]存储最优解
int n = 6;void MatrixChain()
{int i, r, j, k;for (int i = 1; i <= n; i++) { m[i][i] = 0;//对角线为0;规模为1时;}for (r = 2; r <= n; r++)//矩阵连乘的规模为r ,从2开始{for (i = 1; i <= n - r + 1; i++)//最大可起始点{j = i + r - 1;//j-i=r-1;m[i][j] = 0x7f7f7f7f;//另开始时m[i][j]为无穷大;s[i][j] = i;//s[][]存储各子问题的决策点,即一开始k为该点。for (k = i; k < j; k++)//寻找最优值{int t = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j];//求出分割点为k时的乘积if (t < m[i][j])//求出最小值,将分割点s[i][j]设置为k;{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()
{MatrixChain();print(1, n);cout << endl;cout << m[1][n] << endl;
}