递归思想和动态规划思想
递归:
1.介绍
递归算法是一种直接或者间接调用自身函数或者方法的算法。其中调用分为直接调用和间接调用,直接调用是指在函数体中调用自身,间接调用是调用别的函数,而这些函数调用函数本身。这样可以把很长的冗余的代码进行了简化。
递归算法解决问题的特点:
-
递归就是方法里调用自身。
-
在使用递增归策略时,必须有一个明确的递归结束条件,称为递归出口。
-
递归算法解题通常显得很简洁,但递归算法解题的运行效率较低。所以一般不提倡用递归算法设计程序。
-
在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等,所以一般不提倡用递归算法设计程序。
递归算法要求。递归算法所体现的“重复”一般有三个要求:
(1) 是每次调用在规模上都有所缩小(通常是减半);
(2) 是相邻两次重复之间有紧密的联系,前一次要为后一次做准备(通常前一次的输出就作为后一次的输入);
(3) 是在问题的规模极小时必须用直接给出解答而不再进行递归调用,因而每次递归调用都是有条件的(例如还没达到一个明确的解或者接受范围是多大),无条件递归调用将会成为死循环而不能正常结束。
2.递归的用处
就举个例子:
计算阶乘:
n! = 1 × 2 × 3 × … × n
但是我们用递归方式就比较简洁:
n! = (n-1)! × n
注意要加上条件,n>=1。
代码实现如下
def factorial(n): if n == 1: return 1 else: return n * factorial(n - 1)
还有斐波那契数列、汉诺塔这些例子我就一一列举了。
动态规划算法:
1.动态规划算法的核心
我在网上找的资料,发现有个写的挺好的,动态规划算法的核心是下面的一张图片和一个小故事。
A * “1+1+1+1+1+1+1+1 =?” *
A : “上面等式的值是多少”
B : “等于8”
A *在上面等式的左边写上 “1+” *
A : “此时等式的值为多少”
B : 很快说“等于9"
A : “你怎么这么快就知道答案了”
A : “只要在8的基础上加1就行了”
A : "所以你不用重新计算因为你记住了第一个等式的值为8,动态规划算法也可以说是 '记住求过的解来节省时间’”
这段话和这个图片还是蛮清晰的,动态规划问题就是记住已经解决过的子问题的解。
2.动态规划算法的介绍
动态规划是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。
这样不是回到递归了吗?,是的。但是你需要知道如何先拆分问题,而拆分问题,靠的就是状态的定义和状态转移方程的定义。
1.状态的定义
先看一道题:
给定一个数列,长度为N, 求这个数列的最长上升(递增)子数列(LIS)的长度. 以 1 7 2 8 3 4 为例。
这个数列的最长递增子数列是 1 2 3 4,长度为4; 次长的长度为3, 包括 1 7 8; 1 2 3 等。
要解决这个问题,我们首先要定义这个问题和这个问题的子问题。
有人可能会问了,题目都已经在这了,我们还需定义这个问题吗?需要,原因就是这个问题在字面上看,找不出子问题,而没有子问题,这个题目就没办法解决。
现在先试试重新定义这个问题:
给定一个数列,长度为N,设FkF_{k}Fk为以数列中第k项结尾的最长递增子序列的长度. 求F1F_{1}F1…FNF_{N}FN中的最大值。
这个新问题与原问题等价。
而对于FkF_{k}Fk来讲,$F_{1} $… Fk−1F_{k-1}Fk−1都是FkF_{k}Fk的子问题:因为以第k项结尾的最长递增子序列(下称LIS),包含着以第1…k-1中某项结尾的LIS。
上述的新问题FkF_{k}Fk也可以叫做状态,定义中的“FkF_{k}Fk为数列中第k项结尾的LIS的长度”,就叫做对状态的定义。
之所以把FkF_{k}Fk做“状态”而不是“问题” ,一是因为避免跟原问题中“问题”混淆,二是因为这个新问题是数学化定义的。
2. 状态转移方程的定义?
上述状态定义好之后,状态和状态之间的关系式,就叫做状态转移方程。
比如,对于LIS问题,我们的第一种定义:
设F_{k}为:以数列中第k项结尾的最长递增子序列的长度.
设A为题中数列,状态转移方程为:
F_{1} = 1 (根据状态定义导出边界情况)
F_{k}=max(F_{i}+1 | A_{k}>A_{i}, i\in (1…k-1)) (k>1)
用文字解释一下是:
以第k项结尾的LIS的长度是:保证第i项比第k项小的情况下,以第i项结尾的LIS长度加一的最大值,取遍i的所有值(i小于k)。
第二种定义:
设F_{i, k}为:在数列前i项中,长度为k的递增子序列中,最后一位的最小值
设A为题中数列,状态转移方程为:
若A_{i}>F_{i-1,k-1}则F_{i,k}=min(A_{i},F_{i-1,k}) 否则:F_{i,k}=F_{i-1,k}
(边界情况需要分类讨论较多,在此不列出,需要根据状态定义导出边界情况。)
大家套着定义读一下公式就可以了,应该不难理解,就是有点绕。
这里可以看出,这里的状态转移方程,就是定义了问题和子问题之间的关系。
可以看出,状态转移方程就是带有条件的递推式。
3.动态规划算法的方法
实现动态规划有两种方法
1.直接自顶向下实现递归式,并将中间结果保存,这叫备忘录法;
2.按照递归式自底向上地迭代,将结果保存在某个数据结构中求解。
具体的话。
具体的例子感那段话还是很清晰的,就不详细列举了。
参考链接:https://www.geek-share.com/detail/2702235748.html
https://www.geek-share.com/detail/2711130709.html
https://www.zhihu.com/question/23995189
- 递归思想,动态规划思想学习(待更)
- 动态规划入门(四)DP 基本思想与实现
- 斐波那契系列问题的递归和动态规划
- POJ 1579 解法二用动态规划给递归剪枝,减少重复计算。此题一开始没想到用此法耗费了不少时间。
- 动态规划基本思想
- 从暴力递归到动态规划的转换(推荐)
- 滑雪 POJ - 1088 记忆化搜索=搜索的形式+动态规划的思想
- LeetCode动态规划思想:Best Time to Buy and Sell Stock III
- leetcode 第32题:动态规划思想与堆栈的灵活使用
- 动态规划的思想
- 算法原理与实践(动态规划与递归)
- 递归和动态规划的转换
- 动态规划与递归非递归
- 动态规划+递归(斐波那契数)
- 动态规划和递归求lcs
- 我的第一个动态规划程序(试图用递归求斐波拉契数)
- 重新认识动态规划以及递归
- POJ1141 Brackets Sequence (dp动态规划,递归)
- POJ 1088 滑雪 ( DFS+动态规划思想 )
- 动态规划入门-数字三角形(从朴素递归到各种优化)