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)的权重为,你可以从每一行的任何一列,直角对角向下移动,一次一格。请问从第一行到最后一行,最少消耗是多少?
递推公式:
填表后的结果:
第一次的代码我都删掉了,这里只有图片:
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就是下面的这个表格:
价值 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
最少硬币数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 2 | 3 | 4 | 5 | 6 | 1 | 2 | 3 | 4 | 5 | 3 | 4 | 5 |
比如说在寻找30的时候,会出现三种情况:
- 30-1=num_coins(29)+1 = 5 √
- 30-10=num_coins(20)+1 = 2 √
- 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是递归!