算法设计与分析 | 动态规划

1,442 阅读5分钟

动态规划

主要内容

  • 动态规划的基本性质和步骤
  • 最大子数组问题
  • 0-1背包问题
  • 旅行商问题
  • 状态压缩动态规划

引例:斐波那契数列

image.png image.png 从图片中不难看出,递归算法自顶向下的计算思路中,存在大量重复计算,当n的取值过大时,递归需要占据大量的时间和空间,需要优化。

解决方法 采用自底向上的计算方法,从第一个元素开始计算,从而消除重复计算。

image.png

基本性质

  1. 和分治类似:动态规划也是将原问题分解为子问题求解。
  2. 和分治不同:不是通过递归的方式,而是自底向上的求解问题。

机器人行走

image.png 还记得吗?这个题目我们在分治一章中讨论过,只能向右或向上行走,则到达终点的路径条数path(i,j) = path(i-1,j) + path(i,j-1).

  • 分治需要计算多少次?55次。

image.png

  • 动态规划需要的计算次数:12次。 image.png

动态规划适用的场景:

  • 子问题并不独立,即子问题是可能重复的。
  • 主要用于优化问题(求最优解),且问题具有最优子结构性质

什么是最优子结构性质?

最优子结构性质指原问题的最优解一定包含了子问题的最优解。例:最短路径问题具有最优子结构性质,最长路径问题则不具备。

基本步骤

  1. 定义子问题,并分析最优解的结构性质:分治通常是将原问题对半分,而动态规划是将n规模的问题分解成n-1规模的问题。
  2. 找出最优解对应的最优值,并递归地定义最优值。
  3. 以自底向上的方式计算出最优值
  4. 根据计算最优值得到的信息,构造最优解。

最大子数组问题

image.png image.png image.png image.png image.png image.png

0-1背包问题

0-1背包问题:给定n种物品和一个背包,物品i的重量是wi,其价值为vi,背包的容量为C(总承重为C),问应如何选择装入背包的物品,使得装入背包中的物品的总价值V最大化。

分析0-1背包问题的最优解的结构特征

  • 一种情况是第n个物品不包括在最优解里:设x* = (x1,x2,...,xn-1,xn),则(x1,x2,...,xn-1)必为n-1个物品(剔除第n个物品),且背包容量为C情况下的最优解。
  • 第二种情况是第n个物品包括在最优解里面:则(x1,x2,...,xn-1)必为n-1个物品,背包容量为C-wn情况下的最优解。

找出0-1背包问题最优解对应的最优值,并递归地定义最优值

在n个物品里,背包容量为C情况下的总价值,我们用m(n,C)表示最优值。 image.png

自底向上地求解最优值

image.png

image.png

根据m值矩阵得出最优解

所以最优解为{a,b,c},最优值为16。

0-1背包最优解
for i=1 to n do
  if m(i,c)==m(i-1,c) then
    x[i]=0
  else
    x[i]=1
    c=c-w[i]
  end if
end for
return x

算法复杂度分析: 从m(i,j)的递归式容易看出,算法需要O(nc)的计算时间。当背包容量C很大时,算法需要的计算时间较多。例如,当C>2^n时,算法需要Ω(n2^n)计算时间。

旅行商问题

image.png

旅行商问题最优解的结构特征

  • 最优子结构性质:假设路径c1c2...cn-1cnc1是城市{c1,c2,...,cn-1,cn}的最短环路,取这个路径的任何一个子路径,如c1c2...ci必然是城市{c1,c2,...,ci}中从c1到ci经过其他城市一次且仅一次的最短路径。
  • 旅行商问题的子问题是显然重叠的:如下两个路径c1c2c3c4...cn和c1c3c2c4...cn都具有相同的子路径c4...cn-1cn。

旅行商问题最优解对应的最优值,并递归地定义最优值

image.png

image.png

自底向上求解最优值

image.png

image.png

image.png

image.png

image.png 在此算法中,因TSP(c1,C,ci)中的c1可忽略,我们用一个二维数组TP[C][ci]存储TSP(c1,C,ci)的值,算法总复杂度为O(n^2 * 2^n)

构造最优解

image.png while循环(语句4-18)依次往最短路径添加城市,每次添加实际上就是找出下一层哪一个TSP和当前城市c^形成了最短回路。

最长公共子序列

image.png

image.png

最长公共子序列的结构

image.png

子问题的递归结构

image.png

自底向上计算c[i][j]

image.png

计算最优值

image.png 由于在所考虑的子问题空间中,总共有Θ(mn)个不同的子问题,因此,用动态规划算法自底向上地计算最优值能提高算法的效率。

构造最长子序列

image.png

状态压缩动态规划

  • 集合状态压缩:用二进制表示集合,之后用整型表示二进制,如旅行商问题中的TP数组。
  • 空间状态压缩:自底向上的方法求解最优值的过程中,压缩最优值的存储空间。

集合状态压缩

整数的一些基本位操作

  • 判断第i比特是否为0:D&(L<<i)==0,若真,则为0,如假,则为1,其中L<<i表示将L左移i位;
  • 将第i比特设置为1:D|(L<<i);
  • 将第i比特设置为0:D&(~(L<<i);
  • 将第i比特取反:D⊕(L<<i);
  • 取出第i比特:D&(L<<i);

image.png

image.png

image.png 需要判断一下:j所代表的城市集合C是否包含了i所代表的城市,如果不包含,就无需做任何处理,如包含,则依次比较j集合所有下一层的TP值。

空间状态压缩

image.png

image.png

image.png

image.png