大家好,我是北海。
数模竞赛中常常会有最优化问题,例如求最短路径、最大容量、钢条切割等问题,而在求解最优化问题所用的模型算法,常常会涉及到动态规划思想。
本文是讲解动态规划思想的第一节,从一个简单的问题开始引入,后续文章会逐渐详细讲解动态规划的原理、具体步骤和代码。
看完本文,如果能回答出下面两个问题,说明你已经初步理解了动态规划:
1、什么是最优化问题?
2、动态规划和递归有什么联系和区别?
(答案见下期文章)
1 问题的提出
假设你正在爬楼梯,到达楼顶总共有100个台阶。每次你只可以爬1或2个台阶。有多少种不同的方案可以爬到楼顶呢?
(LeetCode No.70)
2 问题分析
1、题目是一组变量(台阶总数、每次可爬台阶数)在约束条件(每次可以爬1或2个台阶)下,求目标参数(爬到楼顶方案数)的最大值,因此这是一个最优化问题。
2、题目要求每一次可以爬1或2层,
- 方案一:第一次爬到1层,第二次爬到2层,第三次…
- 方案二:第一次爬到1层,第二次爬到3层,第三次…
- 方案三:第一次爬到2层,第二次爬到3层,第三次…
- ……
- 方案N:…
总之,每一次都可以选择爬1或2层,也就意味着每爬完一次,就要继续从两种方法(爬1层还是爬2层)中选一个,直到爬到了第100层。
这咋求总共有多少种方案啊,总不能把所有情况全写出来再数吧?那也太多了。
这时,就要用到了动态规划。
3 动态规划的核心思想
思考:假设现在已经爬到了第x层,那么你前一步是在哪一层?
根据题目要求的“每次你可以爬 1 或 2 个台阶”,前一步一定是在第x-1或者第x-2层;
那么,如果前一步是在第x-1层,那么再往前一步呢?
肯定在x-2或x-3层;那假如是在x-2层,再往前一步呢?那就是在x-3或x-4层;
如此推导下去,一定会推到是在第0层,也就是最初的起点。
另一种情况即当前在第x层、前一步在第x-2也是同理,最后会推到第0层。
因此,设“爬到第x层的方案数”为f(x),就能得到递归式:
f(x)=f(x−1)+f(x−2)
该式的意义:
- 从第0层开始爬到第x层台阶,总共有f(x)种方案;
- 在爬到第x层的前一步,有两种可能:在第x-1,或第x-2层;
- 爬到x-1层有f(x-1)种方案,爬到x-2层有f(x-2)种方案;
- 爬到第x层台阶的方案数,就是爬到x-1层的方案数与爬到x-2层的方案数之和(★★★★★这一点是动态规划的核心思想,就是把问题分解成性质相同、规模更小的子问题)
由这4条,就可得出前面提到的递归式:f(x)=f(x−1)+f(x−2)
可见,我们把“求解f(x)”这一问题,分解成了“求解f(x-1)”和“求解f(x-2)”这两个子问题。
显然f(x-1)和f(x-2)的规模要比f(x)更小,而性质又相同,即都是关于x的相同的函数表达式f。
4 动态规划和递归的联系与区别
有些同学可能有疑惑,这不就是递归么,怎么讲着动态规划反而成了递归了?
动态规划确实用到了递归思想,但与单纯地求解递归还是有差异的,同一问题用动态规划往往比递归更省时间(准确的说法是时间复杂度更低,涉及到数据结构的知识)。
那么动态规划和递归到底有什么区别呢?
在Quora上有一个通俗解释动态规划的50k高赞回答:
问:填空题:1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 =____
答:“8!”
问:要是在最左边再写个“ 1 +”呢?又该填什么?
速答:“9!”
问:“你怎么这么快知道它是9?”
答:“刚刚只加了个1啊”
“所以你 不需要重新计数, 因为你记得原本已经是8了!动态规划只是一种表达'记住东西以节省时间的奇妙方式'”
在这个例子中,在式子最左边加了个1后要求解新问题,就把该问题分解成“新加了1”和“八个1相加等于几”这两个子问题,而且性质相同,都是加法。
其中第二个子问题“八个1相加等于几”,在刚刚已经求解过并且记录下来了,所以能很快地求解出新问题的解。
而如果用递归法,则不会记录子问题答案,会重新开始一个个相加,运算速度肯定会更慢。
这一点涉及到动态规划很重要的一条特点:允许有重叠子问题,且不会重复计算。
本文暂时不展开讲,后续文章会讲到的,这一点在写代码的时候就体现在了“以空间换时间”。
5 边界条件
好,搞明白动态规划的基本思想是“把问题分解成规模更小、性质相同的子问题”之后,又有个问题需要考虑:就这样一直分解下去,什么时候到头啊?总不能一直就无穷无尽地分解下去吧?
这就涉及到边界:
- 不断分析再往前面一步是在哪一层,直到达到边界:若发现前一步在第0层,也就是最初的起点,那么“爬到第0层的方案数”是f(0);
- 根据现实意义,爬楼只能从第0层也就是地面开始爬,题目不允许你飞到某一层开始爬,因此只有一种“原地起步”的方案,那么f(0)=1;
- 接上一条,从第0级到第1级也只有一种方案,即爬一级,得f(1)=1;
前文讲清楚了动态规划的基本思想:分解成子问题、找到边界值,那么问题就迎刃而解:
- 有了边界条件f(0)和f(1)的值,和前面所写的递归式,就能求出f(2)=2,f(3)=3,f(4)=5……一直可以求到f(100),问题就解决了。
以上对递归式意义的分析,就是动态规划的基本思想。
后续文章会讲更严格的定义、求解方法和代码,并对动态规划做出总结。