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

  • 相关阅读:
    RE
    【LeetCode】198. House Robber
    【LeetCode】053. Maximum Subarray
    【LeetCode】152. Maximum Product Subarray
    【LeetCode】238.Product of Array Except Self
    【LeetCode】042 Trapping Rain Water
    【LeetCode】011 Container With Most Water
    【LeetCode】004. Median of Two Sorted Arrays
    【LeetCode】454 4Sum II
    【LeetCode】259 3Sum Smaller
  • 原文地址:https://www.cnblogs.com/wade-luffy/p/7722192.html
Copyright © 2011-2022 走看看