【算法分析】动态规划详解+范例+习题解答
🦄1.动态规划Dynamic Programming
1.1子问题的重叠性质
递归算法求解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次。这种性质称为子问题的重叠性质
1.2 动态规划【自底向上】和分治法【自顶向下】的适用情况
- 不考虑计算效率的情况下,对分治法适用的问题,动态规划也适用;
- 不考虑计算效率的情况下,对动态规划适用的问题,分治法也适用;
- 当子问题具有重叠性质时,动态规划比分治法更有优势;
1.3动态规划的基本思想
- 将求解的较大规模的问题分割成k个更小规模的子问题。
- 对这k个子问题分别求解。如果子问题的规模仍然不够小,则再划分为k个子问题,如此递归的进行下去,直到问题规模足够小,很容易求出其解为止。
- 如果能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,就可以避免大量重复计算,从而提高计算效率。
- 经分解得到的子问题往往不是互相独立的。不同子问题的数目常常只有多项式量级。在用分治法求解时,有些子问题被重复计算了许多次。
动态规划每次总是“自底向上”地求解问题,是否可能存在“多余求解”的情形?是
1.4动态规划基本步骤
- 找出最优解的性质,并刻划其结构特征。
- 递归地定义最优值。
- 以自底向上的方式计算出最优值。
- 根据计算最优值时得到的信息,构造最优解。
🦄2.范例
2.1 矩阵连乘问题
给定n个矩阵{ A 1 A_1 A1, A 2 A_2 A2,…, A n A_n An},其中 A i A_i Ai与 A i + 1 A_i+1 Ai+1是可乘的,i=1,2 ,…,n-1。如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。
2.2.1 基本思想
矩阵连乘计算次序问题的最优解包含着其子问题的最优解。这种性质称为最优子结构性质。
具体:计算A[i:j]的最优次序所包含的计算矩阵子链 A[i:k]和A[k+1:j]的次序也是最优的
设计算A[i:j],1≤i≤j≤n,所需要的最少数乘次数m[i,j],则原问题的最优值为m[1,n]
当i=j时,A[i:j]=Ai,因此,m[i,i]=0,i=1,2,…,n
当i<j时,
可以递归地定义m[i,j]为:
k的位置只有 j-i 种可能
2.2.2 伪代码–分治法
【求解方法分治法】
利用递归式直接计算m[i:j]
static int MatrixChain (int i, int j)
{
if (i == j) return 0;
int u = MatrixChain (i+1,j) + p[i-1]*p[i]*p[j];
for (int k = i+1; k < j; k++) {
int t = MatrixChain (i,k) + MatrixChain ( k+1,j)
+ p[i-1]*p[k]*p[j];
if (t < u) {
u = t;
}
}
return u;
}
2.2.3 伪代码–动态规划法
public static void matrixChain(int [] p, int [][] m )
{
int n=p.length-1;
for (int i = 1; i <= n; i++) m[i][i] = 0;
for (int r = 2; r <= n; r++)# r连乘长度,2个矩阵连乘开始
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];
#r=2时 M12、M23、M34.。。。。
#r=3时 M13、M24、M35.。。。。
👇找例如连乘为3时,里面有两种可能,通过循环找最小的可能结果
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;
}
}
}
}
算法复杂度分析:
算法matrixChain的主要计算量取决于算法中对r,i和k的3重循环。循环体内的计算量为O(1),而3重循环的总次数为O(n3)。因此算法的计算时间上界为O(n3)。算法所占用的空间显然为O(n2)
2.3 重叠子问题
- 递归算法求解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次。这种性质称为子问题的重叠性质。
- 动态规划算法,对每一个子问题只解一次,而后将其解保存在一个表格中,当再次需要解此子问题时,只是简单地用常数时间查看一下结果。
- 通常不同的子问题个数随问题的大小呈多项式增长。因此用动态规划算法只需要多项式时间,从而获得较高的解题效率。
2.4 备忘录方法
备忘录方法的控制结构与直接递归方法的控制结构相同,区别在于备忘录方法为每个解过的子问题建立了备忘录以备需要时查看,避免了相同子问题的重复求解。
2.4.1伪代码
m->0
private static int lookupChain(int i, int j)
{
if (m[i][j] > 0) return m[i][j];
if (i == j) return 0;
int u = lookupChain(i+1,j) + p[i-1]*p[i]*p[j];
s[i][j] = i;
for (int k = i+1; k < j; k++) {
int t = lookupChain(i,k) + lookupChain(k+1,j) + p[i-1]*p[k]*p[j];
if (t < u) {
u = t; s[i][j] = k;}
}
m[i][j] = u;
return u;
}
2.5 公共子序列
给定2个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。
设序列X={x1,x2,…,xm}和Y={y1,y2,…,yn}的最长公共子序列为Z={z1,z2,…,zk} ,则
1)若xm=yn,
则zk=xm=yn,且Zk-1是Xm-1和Yn-1的最长公共子序列。
2)若xm≠yn,
则Z是
Xm-1和Y的最长公共子序列,X和Yn-1的最长公共子序列,
中较长的序列
最长公共子序列👇
c表示最长匹配数
2.5.1公共子序列例子
若给定序列X={x1,x2,…,xm},则另一序列Z={z1,z2,…,zk},是X的子序列是指存在一个严格递增下标序列{i1,i2,…,ik}使得对于所有j=1,2,…,k有:zj=xij。
例,假设
序列 X = {A,B,C,B,D,A,B},
序列 Y = { B,C, D, B},
则Z是X的子序列,相应的递增下标序列为{2,3,5,7}。
🦄3.习题
3.1 矩阵相乘
-
矩阵A[mn],B[nt]相乘时,按照传统乘法,一共需要进行多少次元素间的相乘?
mnt -
设m[i,j]表示乘积Ai…Aj中所需最少的元素乘法数量,
易知:m[4,5]=1000,m[5,6]=4000
那么,m[4,6]=[填空1]。
1)m[4,5] +(A45)A6 =1000 + 52020 =3000 -
设m[i,j]表示乘积Ai…Aj中所需最少的元素乘法数量,
已知:m[3,4]=750,m[3,5]=2500,
m[4,5]=1000,m[4,6]=3000,m[5,6]=4000
那么,m[3,6]=[填空1]。
m[3,3] +m[4,6] + 15520 =0+3000+1500 =4500 -
最优值
🦄4.书后习题
3-1、3-4、3-2