zoukankan      html  css  js  c++  java
  • 动态规划

    动态规划(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时,迭代算法并不比递归算法性能好。

  • 相关阅读:
    Scala泛型
    Tensorflow激活函数
    20181030-4 每周例行报告
    20181023-3 每周例行报告
    20181016-10 每周例行报告
    20181009-9 每周例行报告
    第三周作业(4)——单元测试
    第三周作业(5)——代码规范
    第三周作业(2)——功能测试
    第三周作业(3)——词频统计--效能分析
  • 原文地址:https://www.cnblogs.com/wade-luffy/p/7722192.html
Copyright © 2011-2022 走看看