我是靠谱客的博主 和谐摩托,最近开发中收集的这篇文章主要介绍【算法设计与分析】动态规划算法学习笔记,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

        • 1. 切割绳子问题
        • 2. 最长公共子序列问题(LCS)
        • 3. 图像压缩
        • 4. 最优二叉检索树
        • 5. RNA二级结构预测

        • 背包问题
        • 序列DP(线性DP)
        • 区间DP
        • 树上DP
        • 状态压缩DP

动态规划的基本原则:最优子结构


1. 切割绳子问题

设定:给定总绳子长度,以及长度为i的绳子对应的价值为p[i];求如何分割使得总价值最大。

长度为i的绳子;可分j段

d p [ i ] = max ⁡ 1 ≤ j ≤ i ( d p [ i − j ] + p [ j ] ) dp[i] = maxlimits_{1le j le i} (dp[i-j]+p[j]) dp[i]=1jimax(dp[ij]+p[j])
最优子结构:局部最优最终会保证整体最优。

动规重要特征:将递归转化为非递归,从而降低时间复杂度(子问题只计算一次)(用记忆化数组进行存储;从而用空间换时间)



2. 最长公共子序列问题(LCS)

  • 注意:子序列不一定要在原序列中连续排列

d p [ i ] [ j ] dp[i][j] dp[i][j] 表示序列1从位置i开始,序列2从位置j开始的最长公共子序列长度。
d p [ i ] [ j ] = { d p [ i + 1 ] [ j + 1 ] + 1 , i f   s 1 [ i ] = s 2 [ j ] max ⁡ ( d p [ i + 1 ] [ j ] , d p [ i ] [ j + 1 ] ) , e l s e dp[i][j] = begin{cases}dp[i+1][j+1]+1 , if space s_1[i] = s_2[j] \ \ max(dp[i+1][j],dp[i][j+1]),else end{cases} dp[i][j]=dp[i+1][j+1]+1,if s1[i]=s2[j]max(dp[i+1][j],dp[i][j+1]),else

  • 初始化(边界条件): d p [ l 1 − 1 ] [ j ] dp[l_1-1][j] dp[l11][j] d p [ i ] [ l 2 − 1 ] dp[i][l_2-1] dp[i][l21]

  • 使用从前向后的正向顺序也是可以的。

  • 循环体内的时间复杂度为 O ( m n ) O(mn) O(mn)


Q:如果想要输出最长公共子序列呢?

使用标记函数:每个子问题取最优时的情况(三种分类)

追踪解:通过标记数组B的情况递归调用数组 Search(i,j)

1)若是第一种情形( s 1 [ i ] = s 2 [ j ] s_1[i] = s_2[j] s1[i]=s2[j]),则直接输出 s 1 [ i ] s_1[i] s1[i],然后继续调用 s e a r c h ( i − 1 , j − 1 ) search(i-1,j-1) search(i1,j1);

  1. 若为第二/三种情形,则分别调用search(i,j-1)或search(i-1,j)
  • 可知追踪解的时间复杂度为 O ( m + n ) O(m+n) O(m+n)

3. 图像压缩

黑白图像存储:灰度值序列(每个像素的灰度值占8位,0-255)

Q:经常出现大面积相同颜色的区域,能否用更好的方式来节省存储空间?灰度值很低的地方是否只需要更少的位来存储?


变位压缩:把灰度值序列分为若干段,同一段的灰度值用相同的位数存储

对第t段而言,需要记录该段像素个数len[t]和每个像素占用位数bit[t] (共11位在段头记录,考虑每段的额外开销)

总占用位数为: ∑ i = 1 m l e n [ i ] ⋅ b i t [ i ] + 11 m sumlimits_{i=1}^m len[i]cdot bit[i]+11m i=1mlen[i]bit[i]+11m

Q:如何确定序列的划分来最小化总位数?

组合优化问题:约束条件: b i t [ t ] = ⌈ log ⁡ 2 ( max ⁡ p k ∈ s t p k + 1 ) ⌉ ≤ 8 bit[t] = lceil log_2(maxlimits_{p_k in s_t}p_k+1)rceil le 8 bit[t]=log2(pkstmaxpk+1)8

Q:如何界定子问题?
d p [ i + 1 ] = min ⁡ 1 ≤ j ≤ i { d p [ j ] + ( i − j + 1 ) ⋅ ⌈ log ⁡ 2 ( max ⁡ j + 1 ≤ k ≤ i p k + 1 ) ⌉ + h e a d e r } dp[i+1] = minlimits_{1le j le i}{dp[j]+(i-j+1)cdot lceil log_2(maxlimits_{j+1le k le i}p_k+1)rceil +header} dp[i+1]=1jimin{dp[j]+(ij+1)log2(j+1kimaxpk+1)+header}
具体实现:对固定i之后的内部循环,可用bmax记录当前像素的最大值(最后向前,依次比较更新bmax值)

追踪解:向前追踪,用标记数组记录总长度取最小值时最后一段的长度。


4. 最优二叉检索树

需要考虑数据元素存取的概率分布

输入:给定序列 S = { x i , 1 ≤ i ≤ n } S={x_i,1le i le n} S={xi,1in}。记 x x x x i x_i xi的概率为 b i b_i bi x x x落在 ( x i , x i + 1 ) (x_i,x_{i+1}) (xi,xi+1)内的概率为 a i a_i ai。即用 { a 0 , a 1 , … , a n + 1 , b 1 , b 2 , … , b n } {a_0,a_1,dots,a_{n+1},b_1,b_2,dots,b_n} {a0,a1,,an+1,b1,b2,,bn}存储S的概率分布。

目标:给出已知概率下平均次数最小的BST(如何组织结点 x i x_i xi


子问题划分:(利用树的递归定义,讨论根结点,左右子树深度+1)

起初想法:如考虑子问题边界(i,j),则需改变概率 a i − 1 a_{i-1} ai1 a j a_j aj (将边界外的值加总,即 a i − 1 ′ = ∑ k = 1 i − 1 a k a_{i-1}' = sumlimits_{k=1}^{i-1}a_k ai1=k=1i1ak);但实际上只需要截取部分概率即可,也便于计算,这是因为合并子问题时可以直接合并边界。


写出状态转移方程如下:
d p [ i ] [ j ] = min ⁡ i ≤ k ≤ j { d p [ i ] [ k − 1 ] + p [ i ] [ k − 1 ] + b k + d p [ k + 1 ] [ j ] + p [ k + 1 ] [ j ] } = min ⁡ i ≤ k ≤ j { d p [ i ] [ k − 1 ] + d p [ k + 1 ] [ j ] + p [ i ] [ j ] } begin{aligned} dp[i][j] &= min_{ile k le j} { dp[i][k-1] +p[i][k-1]+b_k+dp[k+1][j]+p[k+1][j] } \ &= min_{ile k le j} { dp[i][k-1]+dp[k+1][j]+p[i][j] } end{aligned} dp[i][j]=ikjmin{dp[i][k1]+p[i][k1]+bk+dp[k+1][j]+p[k+1][j]}=ikjmin{dp[i][k1]+dp[k+1][j]+p[i][j]}
其中 d p [ i ] [ j ] dp[i][j] dp[i][j] 考虑的即为子区间 ( x i − 1 , x j + 1 ) (x_{i-1},x_{j+1}) (xi1,xj+1) 上的情况,最终目标为 d p [ 1 ] [ n ] dp[1][n] dp[1][n] p [ k ] [ l ] p[k][l] p[k][l]表示取值为 ( x k − 1 , x l + 1 ) (x_{k-1},x_{l+1}) (xk1,xl+1)的概率,有:
p [ k ] [ l ] = ∑ i = k − 1 l a i + ∑ j = k l b j p[k][l] = sumlimits_{i =k-1}^{l}a_i +sumlimits_{j=k}^l b_j p[k][l]=i=k1lai+j=klbj

  • 注意:边界条件为 d p [ i ] [ i − 1 ] = 0 dp[i][i-1] = 0 dp[i][i1]=0!(从而无需对 j = i+1的情形进行单独讨论)

时间复杂度为 O ( n 3 ) O(n^3) O(n3)。本题思路和矩阵链乘法问题类似。



5. RNA二级结构预测

输入:由A C G U标记的核苷酸构成的一条链。

二级结构:核苷酸相互匹配构成的平面结构(有臂状、环状)

目标:希望预测RNA的平面二级结构

原则:碱基配对;末端不出现“尖角”;只能单向配对;不能交叉

  • 对子问题dp[i] [j],考虑所有能和j位置元素配对的k位置,拆分为两个子问题

C [ i ] [ j ] = m a x { c [ i ] [ j − 1 ] , max ⁡ i ≤ k ≤ j − 4 1 + C [ i ] [ k − 1 ] + C [ k + 1 ] [ j − 1 ] } C[i][j] = max{c[i][j-1],max_{ile k le j-4}1+C[i][k-1]+C[k+1][j-1]} C[i][j]=max{c[i][j1],maxikj41+C[i][k1]+C[k+1][j1]}


总结

  • 引入参数,界定子问题边界

  • 设计优化函数,验证是否满足优化原则

  • 用备忘录记录优化函数的值,自底向上迭代计算

  • 用标记函数进行记录,从而便于追踪最优解。

DP算法时间效率高的根本原因:“空间换时间”,当空间要求较大时则往往成为制约因素。


背包问题

空间复杂度的降低:01背包可转化为一维滚动数组,逆向求解

多重背包:转化为0/1背包

状态转移方程: d p [ i ] [ j ] = d p [ i − 1 ] [ j − k ∗ w [ i ] ] + k ∗ v [ i ] , 0 ≤ k ≤ j w [ i ] dp[i][j] = dp[i-1][j-k*w[i]]+k*v[i],0le k le dfrac j{w[i]} dp[i][j]=dp[i1][jkw[i]]+kv[i],0kw[i]j

优化:可以使用二进制进行优化


完全背包:每种物品可以放入无限件

因此写出状态转移方程如下: d p [ i ] [ j ] = max ⁡ ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − w [ i ] ] + v [ i ] ) dp[i][j] = max(dp[i-1][j],dp[i][j-w[i]]+v[i]) dp[i][j]=max(dp[i1][j],dp[i][jw[i]]+v[i])

此处的不同:在考虑是否选择第i件物品这个策略时,不需要限制子结果来自选择前i-1物品(每个物品可以选择无限件)

使用滚动数组进行优化可得:
f o r   i : 1   t o   n   d o    f o r   j : 0   t o   v   d o     d p [ j ] = max ⁡ ( d p [ j ] , d p [ j − w [ i ] ] + v [ i ] ) for i : 1 to n do \ for j : 0 to v do \ dp[j] = max(dp[j],dp[j-w[i]]+v[i]) for i:1 to n do  for j:0 to v do   dp[j]=max(dp[j],dp[jw[i]]+v[i])
理解:此处滚动数组的遍历顺序是正向的。


序列DP(线性DP)

线段覆盖问题

每条线段具有价值,想选择出若干个互不相交的线段,使得总价值最大。

普通覆盖问题:退化为贪心(活动选择问题)

预处理方法:按照右端点排序?(从第一个开始)

状态转移方程:
F [ i ] = max ⁡ { F [ j ] + v [ i ]   ∣   r j ≤ l i } F[i] = max{F[j]+v[i] | r_j le l_i } F[i]=max{F[j]+v[i]  rjli}
优化:只需要找到最大的 j


区间DP

左右端点

石子归并:必须合并相邻的石子


树上DP

二叉苹果树问题:通过两个子树的苹果数进行递推

F [ i ] [ j ] F[i][j] F[i][j] 表示根结点为i,保留j条树枝所能保留的最大苹果树。从而可以进行递推。


状态压缩DP

思路:将问题状态压缩成二进制数

例:最小哈密顿路径问题(TSP问题)

最后

以上就是和谐摩托为你收集整理的【算法设计与分析】动态规划算法学习笔记的全部内容,希望文章能够帮你解决【算法设计与分析】动态规划算法学习笔记所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(63)

评论列表共有 0 条评论

立即
投稿
返回
顶部