动态规划问题
来源:互联网 发布:日期倒计时软件 编辑:程序博客网 时间:2024/06/06 08:48
动态规划问题
动态规划方法通常用来求解最优化问题。问题可以有很多可行解,每个解都有一个值,我们希望寻找具有最优值的解。这样的解为问题的一个最优解(an optimal solution),而不是最优解(the optimal solution),因为可能有多个解都达到最优值。
一、钢铁切割问题
给定一段长度为
- 解法一:
为了求解规模为
通过组合两个相关子问题的最优解,并在所有可能的两段切割方案中选取组合收益最大者,构成原问题的最优解。
称钢条切割问题满足最优子结构性质:问题的最优解由相关子问题的最优解组合而成,而这些子问题可以独立求解。
- 解法二:
递归求解:将钢条从左边切割下长度为
即问题分解的方式为:将长度为
公式:
在此公式中,原问题的最优解只包含一个相关子问题(右端剩余部分)的解,而不是两个。
1. 自顶向下递归实现–解法二
Cut_rod(p, n) if n == 0 return 0 q = -inf for i = 1 to n q = max(q, p[i] + Cut_rod(p, n-i)) return q
- 随着n的增大,程序运行时间越来越长,效率很低,工作量会爆炸性地增长;
- Cut_rod反复用相同的参数值对自身进行递归调用,即反复在求解相同的子问题;
如图:
运行时间:
2n ,为n的指数函数。
2. 动态规划方法求解
动态规划方法仔细安排求解顺序,对于每个子问题只求解一次,并将结果保存下来。
动态规划方法是付出额外的内存空间来节省计算时间,是典型的时空权衡的例子。
两种等价的实现方法:
带备忘的自顶向下法
仍然按照自然的递归形式编写过程,但过程中会保存每个子问题的解。
Memoized_cut_rod(p, n) let r[0..n] be a new array for i = 0 to n r[i] = -inf return Memoized_cut_rod_aux(p, n, r)Memoized_cut_rod_aux(p, n, r) if r[n] >= 0 return r[n] if n == 0 q = 0 else q = -inf for i = 1 to n q = max(q, p[i] + Memoized_cut_rod_aux(p, n-i, r)) r[n] = q return q
自底向上法
将子问题按规模排序,按由小到大的顺序进行求解。
Bottom-up-cut-rod(p,n) let r[0..n] be a new array r[0] = 0 for j = 1 to n q = - inf for i = 1 to j q = max(q, p[i]+r[j-i]) r[j] = q return r[n]
- 两种方法得到的算法具有相同的渐进运行时间;
- 自顶向下的方法并未真正地递归考察所有可能的子问题;
- 由于没有频繁的递归函数调用的开销,自底向上的方法的时间复杂性函数通常具有更小的系数。
3. 重构解
对2中的动态规划算法进行扩展,使之对每个子问题不仅保存最优收益值,还保存对应的切割方案。
其中
Extended-bottom-up-cut-rod(p,n) let r[0..n] and s[0..n] be new arrays r[0] = 0 for j = 1 to n q = - inf for i =1 to j if q < p[i] + r[j-i] q = p[i] + r[j-i] s[j] = i r[j] = q return r and s
长度为n的钢条的完整的最优切割方案:
Print-cut-rod-solution(p,n) (r,s) = extended-bottom-up-cut-rod(p,n) while n > 0 print s[n] n = n - s[n]
二、矩阵链乘法
矩阵链乘法中,在相乘的过程中加括号会对乘积运算的代价产生巨大的影响。
矩阵链乘法问题: 给定
利用动态规划的方法寻找最优括号化方案。
步骤1:最优括号化方案的结构特征
假设
步骤2:一个递归求解方案
令
矩阵
步骤3:计算最优代价
自底向上方法实现:
Matrix_chain_order(p) n = p.length - 1 let m[1..n, 1..n] and s[1..n-1,2..n] be new tables for i = 1 to n m[i,j] = 0 for l = 2 to n for i = 1 to n-l+1 j = i + l - 1 m[i,j] = inf for k = i to j-1 q = m[i,k] +m[k+1,j] + p_{i-1}p_{k}p_{j} if q < m[i,j] m[i,j] = q s[i,j] = k return m and s
时间代价:
空间代价:
算法图解
步骤4:构造最优解
输出最优括号化方案:
Print_optimal_parens(s,i,j) if i == j print "A" else print "(" Print_optimal_parens(s,i,s[i,j]) Print_optimal_parens(s,s[i,j]+1,j) print ")"
调用 Print_optimal_parens(s,1,6)输出括号化方案:
带备忘的自顶向下法:
Memoized_matrix_chain(p) n = p.length - 1 let m[1..n,1..n] be a new array for i = 1 to n for j = 1 to n m[i,j] = inf return Lookup_chain(m,p,1,n)Lookup_chain(m,p,1,n) if m[i,j] < inf return m[i,j] if i == j m[i,j] = 0 else for k = i to j-1 q = Lookup_chain(m,p,i,k) + Lookup_chain(m,p,k+1,j) + p_{i-1}p_{k}p_{j} if q < m[i,j] m[i,j] = q return m[i,j]
时间复杂度:
三、最长公共子序列
Longest-common-subsequence problem.
最长公共子序列问题:给定两个序列
步骤1:刻画最长公共子序列的特征
如果用暴力搜索方法求解LCS问题,就要穷举X的所有子序列,对每个子序列检查它是否也是Y的子序列,运行时间为
步骤2:一个递归解
定义
根据LCS问题的最优子结构性质,可得如下公式:
步骤3:计算LCS的长度
使用动态规划方法自底向上计算。
过程Lcs_length 接受两个序列
它将
Lcs_length(X,Y) m = X.length n = Y.length let b[1..m,1..n] and c[0..m,0..n] be new tables for i = 1 to m c[i,0] = 0 for j = 1 to n c[0,j] = 0 for i = 1 to m for j = 1 to n if x_{i} = y_{j} c[i,j] = c[i-1, j-1]+1 b[i,j] = "^\" #表示图中指向左上的箭头 elseif c[i-1,j] >= c[i,j-1] c[i,j] = c[i,j-1] b[i,j] = "^|" #表示图中指向上的箭头 else c[i,j] = c[i,j-1] b[i,j] = "<-" #表示图中指向左的箭头 return c and b
下图显示了Lcs_length对输入序列
步骤4:构造LCS
可以用Lcs_length返回的表b快速构造
当在表项
Print_lcs(b,X,i,j) if i == 0 or j == 0 return if b[i,j] == "^\" Print_lcs(b,X,i-1,j-1) elseif b[i,j] == "^|" Print_lcs(b,X,i-1,j) else Print_lcs(b,X,i,j-1)
四、最优二叉搜索树
optimal binary search tree.
最优二叉搜索树问题:给定一个n个不同关键字的已排序的序列
对每个关键字
假定一次搜索的代价等于访问的节点数,即此次搜索找到的结点在T中的深度再加1。则一次搜索的期望代价为:
步骤1:最优二叉搜索树的结构
如果一棵最优二叉搜索树T有一棵包含关键字
???没有明白这么表述的意义在哪里。
步骤2:一个递归算法
对于包含关键字
若
e[i,j]的值给出了最优二叉搜索树的期望搜索代价。
步骤3:计算期望搜索代价
表
同时还需要一个w[1..n+1,0..n]的表来保存w(i,j)避免每次计算e[i,j]时重新计算w(i,j).
令
Optimal_bst(p,q,n) let e[1..n+1, 0..n], w[1..n+1, 0..n], and root[1..n, 1..n] be new tables for i = 1 to n+1 e[i,i-1] = q_{i-1} w[i,i-1] = q_{i-1} for l = 1 to n for i = 1 to n-l+1 j = i+l-1 e[i,j] = inf w[i,j] = w[i,j-1] + p_{j} + q_{j} for r = i to j t = e[i,r-1] + e[r+1,j] +w[i,j] if t < e[i,j] e[i,j] = t root[i,j] = r return e and root
时间复杂度:
作者: 大树先生
博客: http://blog.csdn.net/koala_tree
2017 年 09 月 06 日
本文为博主原创文章,未经本人允许不得转载。
- 动态规划----贪心的动态规划问题
- 动态规划问题
- 动态规划问题
- 动态规划回文问题
- 动态规划 背包问题
- 【动态规划】背包问题
- 动态规划问题
- 动态规划问题
- 【动态规划】加油问题
- 动态规划基本问题
- 动态规划-背包问题
- 动态规划问题
- 动态规划基本问题
- 动态规划---->货郎担问题
- 动态规划+背包问题
- 乘法问题(动态规划)
- 动态规划问题
- 动态规划问题
- 基本算法:生成元素数组的所有排列(permute)
- PHP进阶(第五弹:文件系统)
- 排序算法(Java)
- 设计模式--装饰器模式
- java进制转换
- 动态规划问题
- 8086寻址方式
- LeetCode
- java中的编码方式
- Java中三种流行的数据连接池
- linux 安装jdk1.8出现Error occurred during initialization of VM
- Yahoo前端优化十四条军规
- error: failed to push some refs to 'git@github.com:....." Updates were rejected because the remote c
- 查看当前系统的BIOS模式