【算法】动态规划(1)

dbq我又开始学算法了 这次是 毫无压力的学 我觉得很开心~ 

以下都来自于我的的humble opinion~

DP中,递归是top-down,填表是bottom-up.

我之前一直都没有搞懂过,自顶向下和自底向上的区别;(可能我现在的理解也有问题 再说)

自顶向下,就是站在了顶层去思考问题,比如说递归,你只需要给出一定的递推公式,然后从顶层去解决问题,细节的部分(你要考虑)但是不会体现在代码里,相当于将一个比较复杂的问题,提取重复的逻辑,逐渐缩小规模。

自底向上,就是从最底层去思考,从i -> i+1是怎么变化的,也就是dp中的填表,也就是递推公式,是从第一个细节开始的。

在DP中,还有一个比较重要的概念,叫做memoisation,记忆化?我会把这个会叫做备忘录。

在这里举个例子,它的用法。

大家肯定都知道fibonacci数列。备忘录的作用就是:相当于有一个其本身有一个列表f[] = [f(1), f(2), f(3)..],每次需要用的时候直接取值,而不是去调用函数,避免了重复计算。

  • 下图为 不通过备忘录 的call tree:

  • 下图为 通过备忘录的call tree(橘色的点的值直接从cache返回)

>3 在之后自顶向下(递归)的代码中,很有可能会使用到备忘录,来优化(减少重复计算来节省时间)。

1. 直角对角向下 最少消耗

有一个n*n的格子,每个格子在每个点(i, j)的权重为W_{i,j},你可以从每一行的任何一列,直角对角向下移动,一次一格。请问从第一行到最后一行,最少消耗是多少?

递推公式:

填表后的结果:

第一次的代码我都删掉了,这里只有图片:

1.1 简单的自顶向下(递归)

这里有一个bug,range(-1, +1)其实只循环了(-1, 0),所以应该改成(range(-1, 2)

1.2 使用备忘录的自顶向下(递归)

这里的备忘录,是指有一个二维数组,用来存储每次到达i, j的时候,消耗的最小值。比如说

这里的12会被重复计算三次,第一次是16,第二次是17,第三次是7,所以通过备忘录可以避免重复计算。

1.3 自底向上(填表)

2. 换零钱

比如说你有32元,现在有面值1,10,和25的硬币,你最少能换到多少个硬币。

这题的答案是:3个10+2个1->5个硬币

在这里,贪心法是不管用的,因为按照贪心的思路,为了换最少的硬币,肯定是先从面值最大的开始,也就是25,这样一来,得到的硬币数为1个25+7个1=8个硬币。

这就是为什么贪心是 局部最优(在换第一个25的时候,肯定是最优的因为最少的硬币占了最大的面值)

而动态规划 是全局最优。

2.1 简单自底向上(填表)

它求出的num_coins就是下面的这个表格:

价值1234567891011121314151617181920212223242526272829303132
最少硬币数123456789123456789102345612345345

比如说在寻找30的时候,会出现三种情况:

  1. 30-1=num_coins(29)+1 = 5 √
  2. 30-10=num_coins(20)+1 = 2 √
  3. 30-25=num_coins(5)+1 = 5

我觉得这几个练习题出的很巧妙的原因,就是它几乎都考的一个知识点,在第一题直角对角向下中,是与上一行的三个格子的权值进行比较,而在本题中,是与三个硬币面值进行比较,这非常循序渐进。如果硬币的面值变多的话,就相当于,与n个硬币面值进行比较,是换n1, n2, n3....?

上述的代码可以输出最少硬币数,但是无法输出换的是哪些硬币,下面的while函数实现了这点。

其中times是表示比如说在32的时候,是换1元,10元,25元,其中消耗的最小的。index是找是1还是10,25的位置,然后为result的硬币+1,也就是 初始都是000,后来变成100,->200,->210,220,->230,也就是1元2个,10元3个。

这里 times[]里存的是,此时取三个面值的硬币,选择哪个硬币数最少;

3. 01背包问题

这个问题的关键就是:装还是不装?

背包问题的递推公式:

3.1 自顶向下(递推)

3.2 自底向上(填表)

如果你觉得你理解了01背包的公式含义,那么做个小练习吧!根据这个价值表,推算出这5个物品的价值:

答案:20 14 25 19 9

最后我希望你永远不要忘记的一点是:bottom-up是填表,top-down是递归!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值