一文详解动态规划
1 .动态规划的基本思想
-
求解具有某种最优性质的问题 -
动态规划算法和分治法类似,将求解问题分解成若干个子问题,得到子问题的解,然后根据这些子问题的解得到原问题的解 -
保存已经解决的子问题的答案,当需要时间再找出子问题的答案,从而避免了大量的重复计算,节省了大量的时间,用一张表来记录得到的所有子问题的答案,我们不管在以后的计算中当中是否会用到都把结果存放到表中
2 .动态规划设计步骤
1.找出最优解的性质,刻画其结构特征
2.递归定义最优质并且写出动态规划方程
3.自底向上方式计算最优值
4.根据计算最优值的过程,构造最优解
3.矩阵连乘问题
m * n矩阵A与 n * p 矩阵B相乘需要耗费O(mnp)的时间,假设我们需要计算ABC三个矩阵的乘积,有两种方式来计算,第一种A(BC),第二种(AB)C;尽管结果相同,但是从时间消耗的角度来考虑,两者却大不相同。 由于矩阵乘法满足结合律,故计算矩阵的连乘积可以有多种不同的计算次序,可以用加括号的方式来确定。若一个矩阵连乘积的计算次序完全确定,则可以按照此次序反复调用两个矩阵乘法的标准算法来计算出矩阵连乘积,完全加括号的矩阵连乘积可以递归地定义为:(1)单个矩阵是完全加括号的(2)矩阵连乘积A是完全加括号的,则A可以表示为两个完全加括号的矩阵连乘积B和C的乘积加括号,即A=BC,例如,矩阵连乘积A1A2A3A4会有5种不同的完全加括号的方式,通过比较乘法运算计算工作量可以发现在计算矩阵连乘积时,加括号方式对计算量有很大影响。
(1)分解最优解的结构
我们利用所得到的最优子结构,就可以根据子问题的最优解来构造原问题的一个最优解,我们已经看到一个矩阵连乘积问题的非平凡实例的任何解法都需要分割乘积,而且任何最优解都包含子问题实例的最优解,所以,可以把问题分割为两个子问题(最优完全加括号方式),寻找子问题实例的最优解,然后合并这些子问题的最优解,进而构造出一个矩阵连乘积问题实例的一个最优解,也就是必须保证在寻找一个正确的位置来分割乘积时,我们已经考虑过了所有的可能的位置,从而确保了已经检查过的是最优的一个。
(2)建立递归关系
m[i][j]给出子问题的最优解,计算A[i.j]所需要的最少数乘积数,同时还确定了计算A[i,j]的最有次序中断开位置k,在该处分裂乘积后可以得到最优完全加括号方式,定义数组s[i][j]保存k值,在计算出最优值m[i][j]之后,可以递归由s[i][j]构造出相应的最优解。
(3)计算矩阵连乘积动态规划算法
#define NUM 41
int p[NUM];
int m[NUM][NUM];
int s[NUM][NUM];
void Chain(int n)
{
for(int i = 1;i <= n;i++)
m[i][i] = 0;
for(int r = 2;r <= n;r++)
for(int i = 1;i <= n-r+1;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;}
}
}
}
以上算法主要计算量取决于程序中对r,i,k的三重循环,三重循环的总体计算次数为O(n3)算法占用的空间为O(n2),由此可见,动态规划算法比穷举搜索法有效得多。