算法复习—动态规划

动态规划

引言

递归调用自身 ,但是问题的基础解通常是用递归函数的形式来说明的

这种技术采取自底向上的方式递推求值,并把中间结果存储起来,以便将来用于计算所要求的解

从而用来解决许多组合最优化的问题

最长公共子序列问题

输入:两个字符串A,B,长度分别为n,m

输出:A和B最长公共子序列的长度

L[ i , j ]表示a1…a i 和b1…b j 的最长公共子序列的长度

  • i = 0 or j = 0 :L[ i , j ] = 0( 所给的可能同时为空字符串
  • 递推式:
    • L [ i , j ] = { 0 if  ( i = 0  or  j = 0 ) L [ i − 1 , j − 1 ] + 1 if  a i = b j max ⁡ ( L [ i , j − 1 ] , L [ i − 1 , j ] ) if  a i ≠ b j \begin{aligned} L[i,j] = & \begin{cases} 0 & \text{if } (i=0 \text{ or } j=0) \\ L[i-1,j-1] + 1 & \text{if } a_i = b_j \\ \max(L[i,j-1],L[i-1,j]) & \text{if } a_i \neq b_j \end{cases} \end{aligned} L[i,j]= 0L[i1,j1]+1max(L[i,j1],L[i1,j])if (i=0 or j=0)if ai=bjif ai=bj

    • ai = bj : 该位置=左上角值+1

      ai != bj : 该位置=max{ 左边, 上边}

算法 LCS[^ time&space]

for i : 0 to n
  L[i,0] = 0
end for
for j : 0 to m
  L[0,j] = 0
end for
for i:1 to n
  for j: 1 to m
    if ai = bj then L[i,j]= L[i-1,j-1]+1  //等于左上角值+1
    else  L[i,j]= max{ L[i-1,j],L[i,j-1] }  //等于左边和上边中最大的那个
    end if
  end for
end for
return L[n,m]

[^ time&space]:最优解能在Θ( nm )时间和Θ( min{ n,m} )的空间内得到

矩阵链相乘

输入:n个矩阵链的维数对应与正整数数组r[1…n+1] ,其中 r[1…n]是n个矩阵的行数 , r[n+1] 是Mn的列数

输出:n个矩阵链相乘的数量乘法的最少次数

假设有 n+1维数组 r1,r2,…r n+1,这里的ri和r i+1分别是矩阵M i中的行数和列数1

Mi,j表示MiMi+1…Mj的乘积

C[i,j]表示Mi,j相乘的数量乘法的次数

设k是i+1到j之间的任意索引,计算Mi,k-1和Mk,j,那么Mi,j=Mi,k-1 * Mk,j。2

算法MATCHAIN3

for i 1 to n
  c[i,i]=0  //填充对角线do
end for
for d: 1 to n-1  //每一次迭代都进到下一条对角线
  for i: 1 to n-d //每一次迭代都进到对角线的一个新项
    j = i+d
    comment:下列三行计算C[i,j]
    C[i,j]=//先将其初始化为一个非常大的值
    for k: i+1 to j
      C[i,j] = min{ C[i,j], C[i,k-1]+C[k,j]+ r[i]*r[k]*r[j+1] }
    end for
  end for
end for
return C[1,n]

动态规划范式

把子问题的解存储起来以避免重复计算是这种有效方法的基础

一个重要发现是,对于算法考虑的原问题的每一个子问题,算法都计算了一个最优解,称为最优化原理

所有点对的最短路径问题

输入: n * n维矩阵 l [ 1…n, 1…n ],这样有向图G中的边(i,j)的长度为 l [i , j]

输出:矩阵D,使得D[ i, j ]等于 i到 j的距离

di,jk :定义为从i到j,并且不经过{ k+1,k+2, …n }中任何顶点的最短路径长度

  • eg: di,j2 指的是从i到j,除了可能经过顶点1顶点2或者同时经过他们而不经过任何其他顶点的最短路径

  • d i , j k = { l [ i , j ] if  k = 0 min ⁡ ( d i , j k − 1 , d i , k k − 1 + d k , j k − 1 ) if  1 ≤ k ≤ n d_{i,j}^k = \begin{cases} l[i,j] & \text{if } k=0 \\ \min(d_{i,j}^{k-1}, d_{i,k}^{k-1} + d_{k,j}^{k-1}) & \text{if } 1 \leq k \leq n \end{cases} di,jk={l[i,j]min(di,jk1,di,kk1+dk,jk1)if k=0if 1kn

每个矩阵可以用公式Dk[i,j]=min{ Dk-1 [i,j],Dk-1 [i,k] + Dk-1 [k,j] }来计算(含k的表达式理解为第k个矩阵)

在第k次迭代中,第k行和第k列都是不变的,因此可以仅用D矩阵的一个副本来进行计算,例如以下算法

算法FLOYD4

D <-  l //将输入矩阵l复制到D
for k: 1 to n
  for i: 1 to n
    for j: 1 to n
      D[i,j] = min{ D[i,j],D[i,k] + D[k,j] }
end for

0/1背包问题5

输入:物品集合U= { u1,u2,…,un}, 容量为C的背包,体积分别为s1,s2,s3…sn,价值为v1,v2,…,vn。

输出:体积不大于C,最大总价值

V[i,j]表示从前i项取出的装入体积为j的背包的最大价值,要求的值是V[n,c]

易知V[0,j]和V[i,0]都=0

V [ i , j ] = { 0 if  i = 0  or  j = 0 V [ i − 1 , j ] if  j < s i max ⁡ { V [ i − 1 , j ] , V [ i − 1 , j − s i ] + v i } if  i > 0  and  j ≥ s i V[i,j] = \begin{cases} 0 & \text{if } i=0 \text{ or } j=0 \\ V[i-1,j] & \text{if } j < s_i \\ \max\{V[i-1,j], V[i-1,j-s_i] + v_i\} & \text{if } i > 0 \text{ and } j \geq s_i \end{cases} V[i,j]= 0V[i1,j]max{V[i1,j],V[i1,jsi]+vi}if i=0 or j=0if j<siif i>0 and jsi

利用上述公式逐行填充表格即可

算法KNAPSACK6

for i: 0 to n
  V[i,0]= 0
for j: 0 to C
  V[0,j]= 0
for i: 1 to n
  for j: 1 to C
    V[i,j] = V[i-1,j]
    if si <= j  then V[i,j]= max{ V[i,j], V[i-1,j-si]+ vi}
  end for
end for
return V[n,C]

  1. 因为要Mi的列数=Mi+1的行数才能进行相乘 ↩︎

  2. 用这种的方法的总耗费即为Mi,k-1的耗费 + Mk,j的耗费 + 这两个结果相乘的耗费(即 ri *rk*r{ j+1 }) ↩︎

  3. 所需的数量乘法的最少次数可以在Θ(n3)时间和&Theta;(n2)空间找出 ↩︎

  4. 算法的运行时间是Θ(n3),空间复杂性是&Theta;(n2) ↩︎

  5. 背包中不可以包含一个以上的同类物品 ↩︎

  6. 背包问题的最优解能在Θ(n C)的时间和Θ©的空间内(对算法稍作修改)得到 ↩︎

  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值