题目描述+算法思路+伪代码+时间空间复杂度分析
1.动态规划算法的主要步骤
(1)找出最优解的性质,并刻画其结构特征
说人话:就是定义好这些数组的含义。这个最优解,它需要包含哪些部分?是不是具有最优子结构性质?并且分析为什当前的子问题的解是最优子结构
(2)递归地定义最优值
说人话:这一步比较关键,就是要写出来那个递归的等式
(3)以自底向上的方式计算出最优值
说人话:要知道初始值是什么
然后再循环,根据上一步给出的等式,逐渐计算得出所有子问题的解
(4)根据计算最优值时得到的信息,构造最优解
说人话:上面只给出了最优值,但是得到这个最优值的路径还不知道,所以要记录,并且最后递归调用输出路径
2.矩阵连乘
2.1题目描述
矩阵连乘的计算规模与复杂度与矩阵本身的大小以及矩阵连乘的方式顺序有关,试找出一串连乘矩阵中连乘次数最小的次数以及当前情况下连乘的顺序
2.2算法思路
按照动态规划算法的主要思路:
2.2.1定义各数组含义,刻画结构特征
(1)定义矩阵
我们要求A1A2A3A4…An连乘的次数,抽象化为计算一般问题A[i:j]的最优计算方法,所以如果要求A1A2A3A4…An的最优计算方法,即求A[1:n]的最优计算方法。
(2)定义最优解的乘法次数
我们现在只表示了矩阵,但是问题是要求最少的连乘次数。顺其自然就想到了A[i:j]最少连乘次数用m[i][j]来表示。所以如果要求A1A2A3A4…An的最优计算方法,即求m[i][j]。
(3)定义最优解的组成
我们知道了A1A2A3A4…An的最优计算方法是求m[1][n],那到底是怎么分的??是从哪里断开,然后前后两个大矩阵相乘,使得连乘次数最少的?
假设是从Ak这里断开的,也就是说计算A[1:k]和A[k+1:n],然后前后两个矩阵相乘得到A[1:n]。如果按照这样计算的话,那么计算总量就是A[1:k]的计算量加上A[k+1:n]的计算量,再加上A[1:k]和A[k+1:n]相乘的计算量。
到现在我们已经知道有了一个分割点之后该怎样去定义A[1:n]的计算量,但关键是这到底是不是最优解呢?所以这里定义了最优解的组成,即:
计算A[1:n]的最优次序所包含的计算子矩阵A[1:k]和A[k+1:n]的计算次序也是最优的
这个是显然成立的,因为但凡这两个子矩阵中有一个不是最优的,那么一定能找到一个最优的,使得计算A[1:n]的次序比当前更优。
2.2.2写出等式,建立递归关系
先从简单的入手。
我们要求的是m[1][n],一般问题是m[i][j],如果能把任意的m[i][j]表示出来,那么m[1][n]当然也没有问题了
分析i,j
当i=j时,A[i:j]=A,所以m[i][i]=0;
当i<j时,利用最优子结构性质来计算m[i][j],即:
m[i][j]=m[i][k]+m[k+1][j]+p[i-1]p[k]p[j]
注:这里的p数组用于记录每个矩阵的维度,比如A1矩阵,则行数为p0,列数为p1;A2矩阵,则行数为p1,列数为p2
综上两种情况:
m[i][j]=0 i=j;
=min{m[i][k]+m[k+1][j]+p[i-1]p[k]p[j]} i<j
2.2.3给出初始值,自底向上得出解
先初始化m[i][j]矩阵,然后循环,按照给出的递归等式,由各种基础情况算出更复杂情况的解
2.2.4构造最优路径
在整个过程中,用一个数组s[i][j]去记录A[i:j]中最佳的分割点,然后利用递归,打印出这些路径
2.3伪代码
void MatrixChain(int p[],int m[][n],int s[][n])
{
//设定初值,A[i:i]不用乘,所以计算次数为0
for(int i=1;i<=n;i++)
m[i][i]=0;
for(int r=2;ir<n;r++)
{
for(int i=1;i<n;i++)
{
int j=i+r-1;
//这里只是随便分了一下,后面反正都要循环遍历,找最小的
//如果有更优的分法,就会把下面的这两个换掉
m[i][j]=m[i+1][j]+p[i-1]*p[i]*p[j];
s[i][j]=i;
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;
}
}
}
}
}
2.4时间空间复杂度分析
三重循环,则时间复杂度为O(n^3);
二维数组,则空间复杂度为O(n^2)