动态规划理解

参看文章:通过金矿模型介绍动态规划

原文写的非常通俗易懂,我只是按自己的理解总结一下。

01背包问题

有10件物品,编号为0-9,第i件物品的体积为weight[i],价值为value[i]。如果有一个背包,容量为s。选取哪些物品装进背包里能使背包的装的东西总价值最大?

因为物品不能拆分,要么整个装,要么整个都不装,所以是01问题。

按平均价值是错误的

看到这个问题,可能首先会想到算每个物品的平均价值,先选平均价值最大的。但是这样算是错的。举个粒子:要把下面3个物品(体积,价值,平均价值)装容量为10的背包:

  物品A(3, 60,20), 物品B(4, 70,17.5), 物品C(5,80, 16)

  按平均价值算:最大平均价值算会选A,B 为 130 ,很显然这是错的。最大价值应该选B,C 150。

如果不是0-1问题,比如物品是一堆糖,一堆盐,是可以拆分,才能用平均价值计算。

暴力遍历组合

最简单粗暴的方法就是遍历所有可能的组合。每个物品都有两种选择:装或者不装。 n件物品总共就有 2种组合。

动态规划

再看看另一种思路, 我们把从i件物品中选择若干件放入容量为j的背包后的最大价值记为f(i,j)。假如背包的容量为20,物品为10件(编号从0-9),我们的任务就是要计算出 f(10,20)。

  先看最后一个物品(编号9,假如体积为3,价值为5),有两种选择:装 或者 不装。 

  (1).如果选择装,并且装得下。那么背包现价值为 5, 背包还能在剩余的9个物品中装进体积为17的东西(20-3)。    背包总价值为:5+f(9,20-3)

  (2).如果不装,那么背包现价值为0, 背包还能在剩余的9个物品中装进体积为20的东西。  背包总价值为:f(9, 20)

  再从这两种选择中选取总价值最大的那一种,f(10, 20)= max{ f(9,20-3)+5,  f(9,20)  }。

  这里还有另外一种情况:如果选择装,但是发现根本装不下,没办法就只能选择不装了,变成了情况(2)。那么最大总价值就是不装的最大总价值 f(10,20)=f(9,20)

  

 这个式子中有两个递归的子问题f(9,17)和f(9,20)。要得到f(10,20)的结果,还得继续算这俩子问题的结果。但是我们发现这俩个子问题和主问题完全是一个套路。比如说f(9,17),不就是在剩下的9个物品中选择一些装进容量为17的背包后的最大价值嘛。也按上面的套路来算好了。

  算f(9,17)。看剩下的这9个物品中的最后一个(也就是8号物品,假如体积为2,价值为3),也是两种选择:装 或者 不装。

  (1).如果选择装,并且装得下。那么背包现价值为 3, 背包还能在剩余的8个物品中装进体积为15的东西(17-2)。    背包最后的总价值为:3+f(8,17-2)。 

  (2).如果不装,那么背包现价值为0, 背包还能在剩余的8个物品中装进体积为17的东西。  背包最后的总价值为:f(8, 20)。

  再从这两种选择中选取总价值最大的那一种,f(9, 17)= max{ f(8,17-2)+3,  f(8,17)  }。 又冒出两个更小点的子问题,还得继续往下算。。。

总结一下从i件物品中选择若干件放入容量为j的背包后的最大价值的递归的公式(背包状态转换方程):

  f(i,j)=max { f(i-1, j-weight(i))+ value(i),   f(i-1, j) } ;   条件  weight(i)<=j

  决策装不装第i个物品的时候另一种情况,如果weight(i)>j,也就是这件物品根本就装不进背包,我们就不考虑能装它的情况啦,背包的最大总价值就是不装该物那种情况的最大总价值。

  f(i,j)= f(i-1,j)   ; 条件 weight(i)>j

递归的子问题在一点点变小,很容易想到,最后一定终结在下面两种情况之一:

  (1)把全部物品都检查看了一遍了,已经没有物品了。 自然有 价值f(0, c)=0

  (2)还有物品,但是背包已经满了。 f(i, 0)=0。

其实也可以更早一点终结: 到只剩下一件物品(编号0)的时候,看还有多大空间的背包(j)。如果j>=weight(0),那么价值就是value(0),  如果j<weight(0),那么价值就是0。

如果直接这样计算,每个问题都划分成两个更小的问题。时间复杂度也是在O(2n)级别。 但实际上有很多f(i,j)值会反复计算多次,可以进一步优化让每个f(i,j)值只计算一次然后保存,以后就不需要再重复计算。然后,复杂度就变成了总共会有多少个子问题f(i,j)需要计算了。理论上说i可以取值0...i,j可以取值0...j,总共i*j+i+j+1种组合,子问题规模为O(ij)。但是很显然,实际的问题数量会远远小于这个理论值。

一般来说动态规划所需要的时间为 T=O(questingCount * chooseCount)。   questingCount(就是前面所说子问题的数量)= 可选对象的数量 * 资源总量。 chooseCount为每个子问题可以进行的选择数。背包问题中,一个物品要么装要么不装,所有chooseCount=2。

   

动态规划的特点(满足这些特点的问题就可以使用动态规划的思想):

  • 最优子结构:如果一个母问题可以分成几个子问题,确定了最优的子问题那么母问题也就是最优的。
  • 子问题重叠:母问题和子问题属于相同的模式,可以用同一个迭代式表示,不同点仅仅是参数。
  • 子问题独立:一个母问题的多个子问题只会选择一个作为实施方案,一般是相互独立的,不会互相影响。
  • 边界:问题逐渐变小,一定要能到达一个终止条件,也就是边界,否则就成死循环了。
  • 缓存中间数据,避免重复计算,可以极大优化性能。

动态规划问题

http://blog.sina.com.cn/s/blog_65caa9780100xrq6.html

原文地址:https://www.cnblogs.com/pixy/p/4748080.html