【算法复习】动态规划

本文介绍了动态规划的基本思想,解决步骤,并以0-1背包问题和最优二叉查找树为例,详细展示了如何定义状态、构造递推公式及求解过程。重点讲解了如何运用动态规划解决最优化问题,包括期望代价计算和构建最小期望代价的最优二叉查找树。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

动态规划

写在前面的话

本文是本学渣因为考试需要写的一篇总结,只总结了需要考察的考点,所以可能有的内容引申的不多,请见谅。

一、思想

动态规划与分治方法类似,都是通过组合子问题的解来来求解原问题的。
分治方法将问题划分为互不相交的子问题,递归的求解子问题,再将它们的解组合起来,求出原问题的解。
动态规划应用于子问题重叠的情况,即不同的子问题具有公共的子子问题(子问题的求解是递归进行的,将其划分为更小的子子问题)。这种情况下使用分治算法会重复求解那些子子问题而浪费时间。

二、解决步骤

解决动态规划问题一般分为四步:
1、定义一个状态,这是一个最优解的结构特征
2、进行状态递推,得到递推公式
3、进行初始化
4、返回结果

三、0-1背包问题

给定n个重量为w1 ,w2 ,…,wn,价值为v1 ,v2 ,…,vn的物品和一个承重为W的背包,求这些物品中能够装到背包中的组合中最有价值的一种组合。

根据上述步骤进行求解:

  1. 定义状态DP[i,j]表示前i个物品装入承重j的背包中物品的最大价值
  2. 情况分析:
    DP[i,j]有两种情况:
    如果不放入第i个物品,则DP[i,j] = DP[i-1,j]
    如果放入第i个物品,则DP[i,j] = DP[i-1,j-wi] + vi
    (i才是主变量,转移方程是i的转移,所以第二种情况是i和i-1的关系,而不是和DP[i,j-1]的关系?)
    (换一种理解,如果装进去第i件商品,那么装入之前是什么状态,肯定是V(i-1,j-w(i))啊)
  3. 因此得出递推关系 DP[i,j] = max{DP[i-1,j], DP[i-1,j-wi] + vi}
  4. 进行初始化:DP[0,j]=DP[i,0]=0
  5. 进行求解
代码表示
# w[i]表示第i个物品的重量, v[i]表示第i个物品的价值, 下标从V开始
# S表示背包总空间, N表示总共多少个物品
Algorithm bag:
    # N+1行S+1列
    dp = [[0 for i in range(S + 1)] for j in range(N + 1)]
    for i in range(1, N+1):
        for j in range(1, S+1):
            if j > w[i]: # 判断此时背包空间是否大于此时物品重量
                dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i])
            else:
            	dp[i][j] = dp[i-1][j]
                
    # 返回在空间为S时前N个物品的最大价值
    return dp[N][S] 

一个非常形象的图解

四、最优二叉查找树

4.1 什么是二叉查找树

二叉查找树(BST:Binary Search Tree)是一种特殊的二叉树,它改善了二叉树节点查找的效率。二叉查找树有以下性质:

对于任意一个节点 n,

  • 其左子树(left subtree)下的每个后代节点(descendant node)的值都小于节点 n 的值;

  • 其右子树(right subtree)下的每个后代节点的值都大于节点 n 的值。

img

4.2 什么是最优二叉查找树

给定n个互异的关键字组成的序列K=<k1,k2,…,kn>,且关键字有序(k1<k2<…<kn),我们想从这些关键字中构造一棵二叉查找树。
对每个关键字ki,一次搜索搜索到的概率为pi
可能有一些搜索的值不在K内,因此还有n+1个“虚拟键”D=<d0,d1,…,dn>,他们代表不在K内的值。
具体为:d0代表所有小于k1的值,dn代表所有大于kn的值。而对于i = 1,2,…,n-1,虚拟键di代表所有位于ki和ki+1之间的值。
对于每个虚拟键,一次搜索对应于di的概率为qi
要使得查找一个节点的期望代价(代价可以定义为:比如从根节点到目标节点的路径上节点数目)最小,就需要建立一棵最优二叉查找树。

image-20210415190039664

4.3 最优二叉树查找的期望代价

对于二叉查找树,期望代价表示如下

E ( T ) = ∑ i = 1 n ( depth ⁡ ( k i ) + 1 ) ∗ p i + ∑ i = 0 n ( depth ⁡ ( d i ) + 1 ) ∗ q i E(T)=\quad \sum_{i=1}^{n}\left(\operatorname{depth}\left(k_{i}\right)+1\right) * p_{i}+\sum_{i=0}^{n}\left(\operatorname{depth}\left(d_{i}\right)+1\right) * q_{i} E(T)=i=1n(depth(ki)+1)pi+i=0n(depth(di)+1)qi

当E(T)得到最小值的时候,则有最优二叉查找树

4.4 最优二叉查找树的最优子结构与动态规划

最优子结构:
如果一棵最优二叉查找树T有一棵包含关键字ki,…,kj的子树T’,那么这可子树T’对于关键字Ki,…,kj和虚拟键di-1,…dj的子问题也必定是最优的。
给定关键字ki,…,kj,假设kr(i<=r<=j)是包含这些键的一棵最优子树的根。其左子树包含关键字ki,…,kr-1和虚拟键di-1,…,dr-1,右子树包含关键字kr+1,…,kj和虚拟键dr,…dj。我们检查所有的候选根kr,就保证可以找到一棵最优二叉查找树。

根据动态规划的解决步骤:

  1. 定义e[i,j]表示包含关键字ki,…,kj和di-1,…,dj的最优二叉查找树的期望代价,最终的目的是求e[1,n]。
    定义e[i,i-1]表示只包含虚拟键di-1的子树(此时只为一个节点)的期望代价,e[i,i-1] = qi-1
    定义w[i,j]表示从ki到kj的子树加上di-1到dj的概率总和,表示为 w [ i , j ] = ∑ l = i j p l + ∑ l = i − 1 j q l . w[i, j]=\sum_{l=i}^{j} p_{l}+\sum_{l=i-1}^{j} q_{l} . w[i,j]=l=ijpl+l=i1jql.
  2. 情况分析:
    根据上述对子结构的分析,当j=i-1的时候,e[i,i-1]=qi-1
    当j>=i的时候, 当一棵树成为另一个节点的子树的时候, 子树中每个节点的深度都加一,导致期望搜索代价增加了那个子树的概率总和,以kr为根的子树的期望代价为:
    e [ i , j ] = 1 ∗ p r + ( e [ i , r − 1 ] + w [ i , r − 1 ] ) + ( e [ r + 1 , j ] + w [ r + 1 , j ] ) e[i, j]=1* p_{r}+(e[i, r-1]+w[i, r-1])+(e[r+1, j]+w[r+1, j]) e[i,j]=1pr+(e[i,r1]+w[i,r1])+(e[r+1,j]+w[r+1,j])
    (新的期望=节点r的期望+(r左边子树的原期望+下移导致增加的期望)+(r右边子树的原期望+下移导致增加的期望))
  3. 因此得出递推公式
    e [ i , j ] = { q i − 1  if  j = i − 1 , min ⁡ i ≤ r ≤ j { e [ i , r − 1 ] + e [ r + 1 , j ] + w ( i , j ) }  if  i ≤ j e[i, j]=\left\{\begin{array}{ll}q_{i-1} & \text { if } j=i-1, \\ \min _{i \leq r \leq j}\{e[i, r-1]+e[r+1, j]+w(i, j)\} & \text { if } i \leq j\end{array}\right. e[i,j]={qi1minirj{e[i,r1]+e[r+1,j]+w(i,j)} if j=i1, if ij
    (对r求最小)
    w [ i , j ] = ∑ l = i j p l + ∑ l = i − 1 j q l . w[i, j]=\sum_{l=i}^{j} p_{l}+\sum_{l=i-1}^{j} q_{l} . w[i,j]=l=ijpl+l=i1jql.
    (e[i,i]直接带入可知)
4.5 伪代码

首先先用一个直观的示意图来解释一下整个过程大概是什么样的:

  1. 每一轮是沿着左上到右下的斜对角线进行的
  2. 设一个变量d表示目前操作距离当前行距离
  3. 求e[i,j]的时候需要如图考虑k取不同值的情况
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aRWkjhjt-1618717259595)(https://gitee.com/DoctorDream/store/raw/master/image/2021/%E6%9C%80%E4%BC%98%20BST%E7%A4%BA%E6%84%8F%E5%9B%BE.png)]
# 动态规划求解最优二叉查找树
# 输入:一个包含n个键对应概率的数组P[1...n],一个包含n个虚拟键对应的概率数组Q[0...n]
# 输出:在最优BST中成功查找的平均比较次数,以及最优BST中子树的根表root

Algorithm OptimalBST(p[1...n],q[0...n]):
    # 初始化保存根节点的root
    root[n][n+1]
    # 初始化只包括虚拟键的子树
    for i ← 1 to n do:
        e[i,i-1] ← q[i-1]
        w[i,i-1] ← q[i-1]
    # 沿着斜对角线向右边推进
    for d ← 1 to n do:
    	for i ← 1 to n - d + 1:
            j ← i + d - 1
            e[i,j]9999999
            w[i,j] ← w[i,j - 1] + p[j] + q[j]
            # 求r移动时取最小期望的时候
            for k ← i to j do:
                temp = e[i,k - 1] + e[k + 1,j] + w[i,j]
                if temp < e[i,j]:
                    e[i,j] = temp
                    root[i][j] = k
    return e[1,n],root
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值