动态规划

动态规划(Dynamic Programming)是一种分阶段求解决策略问题的思想。

动态规划中包含三个重要的概念:

  1. 最优子结构   F(10)=F(8)+F(9)
  2. 边界  n>=3 F(1)=1  F(2)=2
  3. 状态转移公式  F(n)=F(n-1)+F(n-2) 

题目:

有一座高度是10级台阶的楼梯,从下往上走,每跨一步只能向上1级或者2级台阶。要求用程序来求出一共有多少种走法。(此题是动态规划中最简单的问题,因为只有一个变化维度)

比如,每次走1级台阶,一共走10步,这是其中一种走法。我们可以简写成 1,1,1,1,1,1,1,1,1,1。

再比如,每次走2级台阶,一共走5步,这是另一种走法。我们可以简写成 2,2,2,2,2。

解法一:暴力枚举,时间复杂度指数级,利用排列组合的思想,写一个多层嵌套循环遍历出所有可能。

解法二:动态规划。

问题建模:

假设现在你只差最后一步就走到第10级台阶,这时的情况有两种:

从9级走到10级

从8级走到10级

想走到第10级,最后一步必然是从8级或者9级开始。

如果已知0-9级台阶的走法有x种,0-8级台阶的走法有y种,那么0-10级台阶走法有几种?x+y。

F(10)=F(8)+F(9) -> F(n)=F(n-1)+F(n-2)  n>=3

F(1)=1  F(2)=2    

求解问题:

1.递归求解 

int getWay(int n){
  if(n<1) return 0;
  if(n==1) return 1;
  if(n==2) return 2;
  return geyWay(n-1)+getWay(n-2);        
}  

 时间复杂度,高度是n-1,节点数是2的n-1次方,复杂度近似O(2^N)。

备忘录算法:计算过程中可把计算过的n和结果存入hash,当遇到相同参数时直接获取。  复杂度O(N)。

2.迭代求解

递归是自顶向下做递归运算,迭代是自底向上做迭代推导。 空间复杂度O(1)

每一次迭代只需保留之前的两个状态就可以推导出新的状态,不需要像备忘录算法那样保存全部的子状态。

 

题目: 

有一个国家发现了5座金矿(500金/5人,200金/3人,300金/4人,350金/3人,400金/5人),每座金矿的黄金储量不同,需要参与挖掘的工人数也不同。参与挖矿工人的总数是10人。每座金矿要么全挖,要么不挖,不能派出一半人挖取一半金矿。要求用程序求解出,要想得到尽可能多的黄金,应该选择挖取哪几座金矿?

解法一:排列组合

每一座金矿都有挖与不挖两种选择,如果有N座金矿,排列组合起来就有2^N种选择。对所有可能性做遍历,排除那些使用工人数超过10的选择,在剩下的选择里找出获得金币数最多的选择。时间复杂度也很明显,就是O(2^N)。

解法二:动态规划

最优子结构:

如果第五个金矿选择不挖   10个工人5个金矿挖最多黄金->10个工人4个金矿挖最多黄金

如果第五个金矿选择挖      10个工人5个金矿挖最多黄金->(10-第五个金矿所需人数)个工人4个金矿挖最多黄金

状态转移公式:

10人5个金矿的最优选择就是10人4金矿的最优选择+(10-第五金矿所需人数)人4金矿的数量+第五金矿的挖金数量。

金矿数量设为N,工人数设为W,黄金量设为G[0-4],用工人量设为P[0-4]

5做金矿和4做金矿之间的最优选择之间存在的关系:

F(5,10)=MAX(F(4,10),F(4,10-P[4])+G[4])

边界:

N=1 W>=P[0]   得到黄金数量是F(N,W)=G[0]   

N=1  W<P[0]  得到黄金数量是F(N,W)=0     

最后的公式:

F(n,w) = 0    (n<=1, w<p[0]);

F(n,w) = g[0]     (n==1, w>=p[0]);

F(n,w) = F(n-1,w)    (n>1, w<p[n-1])  

F(n,w) = max(F(n-1,w),  F(n-1,w-p[n-1])+g[n-1])    (n>1, w>=p[n-1])

简单递归

把状态转移方程式翻译成递归程序,递归的结束的条件就是方程式当中的边界。因为每个状态有两个最优子结构,所以递归的执行流程类似于一颗高度为N的二叉树。 方法的时间复杂度是O(2^N)。

备忘录算法

在简单递归的基础上增加一个HashMap备忘录,用来存储中间结果。HashMap的Key是一个包含金矿数N和工人数W的对象,Value是最优选择获得的黄金数。 方法的时间复杂度和空间复杂度相同,都等同于备忘录中不同Key的数量。

迭代算法: 

表格第一列代表给定前1-5座金矿的情况,也就是N的取值。

表格的第一行代表给定的工人数,也就是W的取值。

表格其余空白的格子代表N和W对应的黄金获得数,也就是F(N,W)。

第一个金矿,400金/5人  第一行其实就是刚才分析的边界,因此可直接得出结果。

 

第二个金矿,500金/5人 

前四个格子由于人数小于5人,所以是0。

W>=5   F(1,w)=400 p[0]=5 g[0]=500  F(1,w-p[0])=[0-400]->W=10的时候还剩5个人还能挖400

F(2,w) = max(F(1,w),  F(1,w-p[0])+g[0])  

最终表格:

除了第一行以外,每个格子都是前一行的一个或两个格子推导而来。

所以可以只缓存前一行的结果,推导出新的一行。方法的时间复杂度是 O(n * w),空间复杂度是(w)。

和工人数W成正比,递归算法的话只与N有关系,当工人数为1000时,迭代算法并不比递归算法性能好。

 

posted @ 2017-10-24 10:18  wade&luffy  阅读(345)  评论(0编辑  收藏  举报