动态规划算法

动态规划的英文名叫Dynamic Programming,是一种分阶段求解决策问题的数学思想。它不仅用于编程领域,也应用于管理学、经济学、生物学


总结一点就是大事化小,小事化了 

题目:求楼梯阶数

有一座高度是10级台阶的楼梯,从下往上走,每跨一步只能向上1级或者2级台阶。要求用程序来求出一共有多少种走法

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


再比如,每次走2级台阶,一共走5步,这是另一种走法。我们可以简写成 2,2,2,2,2。 
当然,除此之外,还有很多很多种走法。

解决思路: 
1. 假设你只差最后一步就走到第10级台阶,这时候会出现几种情况? 
2. 两种情况 
- 从9级走到10级 
- 从8级走到10级 
3. 想走到10级,最后一步必然是从8级或者9级开始的 
4. 假设0-8级的台阶所有走法是x,0-9级所有台阶的走法是y ,那么走到10级的台阶总数就是x+y! 
这里写图片描述 
5. 为了方便表达,我们用式子表示: 
F(10) = F(9) + F(8); 
6. 以此类推: 
F(9) = F(8) + F(9) 
F(8) = F(7) + F(9)


上面的这种思想就是动态规划思想,将一个复杂的问题分阶段进行简化,一步一步简化成简单的问题,就是动态规划! 

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

  • 最优子结构 : F(9)和F(8)
  • 边界 :只用一级台阶或者是2级台阶,可以直接得出结论,无需继续简化 
    F(1)和F(2)
  • 状态转移公式: 
    F(n)= F(n-1)+F(n-2)

求解方法: 
1. 递归求解

public int getClimbingWays(int n){
    if(n<1) return 0;
    if(n == 1) return 1;
    if(n == 2) return 2;
    return getClimbingWays(n-1) + getClimbingWays(n-2);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

时间复杂度O(2^n): 
这里写图片描述


优化:有好些值被重复计算了 

这里写图片描述

使用哈希表记录不同的计算结果,当遇到相同的参数时,不用计算,直接从哈希表中获取! 
这种暂存计算结果的方式叫*备忘录算法*

  1. 2.
public int getClimbingWays(int n, HashMap<Integer,Integer> map){
    if(n<1) return 0;
    if(n == 1) return 1;
    if(n == 2) return 2;
    if(map.contains(n)){
        return map.get(n);
    }else{
        int value = getClimbingWays(n-1) + getClimbingWays(n-2);
        map.put(n,value);
        return value;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12


时间复杂度:n 
空间复杂度:n 

进一步优化代码:思路逆转过来,采用迭代的思维: 
这里写图片描述 
F(1) = 1 
F(2) = 2 
这是之前就确认过的结果 
这里写图片描述 
第一次迭代,台阶数等于3时,走法数量也为3,这个结果怎末来的,是F(1),F(2)这两个结果相加得到的,所以F(3)只依赖于F(1)和F(2) 
这里写图片描述 
第二次迭代,台阶数等于5时,走法数量也为3,这个结果怎末来的,是F(2),F(3)这两个结果相加得到的,所以F(4)只依赖于F(2)和F(3) 
这里写图片描述 
同理,由此可见,每一次迭代的过程总,只要保留之前的两个状态,就可以推导出新的状态,而不需要像备忘录那样保留全部的子状态

  1. 动态规划求解
public int getClimbingWays(int n){
    if(n < 1) return 0;
    if(n == 1) return 1;
    if(n ==2 ) return 2;
    int a = 1;
    int b = 2;
    int temp = 0;
    for(int i=3; i<=n ; i++){
        temp = a + b;
        a = b;
        b = temp;
    }
    return temp;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

题目二: 国王和金矿

有一个国家发现了5座金矿,每座金矿的黄金储量不同,需要参与挖掘的工人数也不同。参与挖矿工人的总数是10人。每座金矿要么全挖,要么不挖,不能派出一半人挖取一半金矿。要求用程序求解出,要想得到尽可能多的黄金,应该选择挖取哪几座金矿? 
这里写图片描述

方法一:排列组合

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

代码比较简单就不展示了,时间复杂度也很明显,就是O(2^N)。

既然这道题属于动态规划,我们来看看属于动态规划的三个要素:对于这道题 
最优子结构:题目要求出10个工人5个金矿时,挖最多黄金的选择,那么最有子结构应该就是


10个人四个金矿时的,挖黄金的最优 

这里写图片描述

最优子结构和最终问题的关系: 
5个金矿的最优选择,就是(4座金矿10个工人的挖金数量+第五座金矿的挖金数量)和 (前4座金矿10-第五座金矿人数+第五座金矿的挖金数量)

为了方便描述,我们把金矿数量设为N,工人数设为W,金矿的黄金数量设为数组G【】,金矿用工量设为数组p【】。 
那么存在这样的关系:


F(5,10) =( F(4,10),F(4,10-P【4】)+ G【4】)) 

边界:边界就是只有一座金矿,也就是N=1的时候,这时候没得选,只能挖这座唯一的金矿,得到的黄金数量就是G【0】;如果给定的工人数不够挖取第1座金矿的时候,也就是w

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值