赞
踩
我们用动态规划方法解决日常生活中经常遇到的一个问题—爬楼梯问题。
一、问题描述
小明家住在二楼,每次回家都需要经过一个10层台阶的楼梯,小明每次可以选择一步走一级台阶或者一步走两级台阶。请帮小明计算他从楼下到家一共有多少种走法?
打个比方,小明可以选择每次都走一级台阶,那么他回家一共需要走十步,这是其中的一种种走法,不然,他还可以选择一次走两级台阶,那么他回家一共需要走五步,这是另外一种走法,除此之外,还有很多种不同的走法,现在我们要做的是把所有可能的走法数量统计出来。
在正式写程序之前,我们先一起整理一下该问题的解题思路。
首先考虑小明走最后一步的情况,他要么是从第九级台阶再走一级到第十级,要么是从第八级台阶走两级到十级,如下图图一所示,也就是说,要想到达第十级台阶,最后一步一定是从第八级或者第九级台阶开始的。
也就是说,如果已知从地面到第八级台阶一共有X种走法,从地面到第九级台阶,一共有Y种走法,那么从地面走到第十级台阶一定是X和Y之和。
为了描述方便,用F(n)表示第n级台阶的走法数量,根据以上分析,可以得到F(10)=F(9)+F(8)。以此类推,对于F(n),我们可以通过F(n-1)和F(n-2)计算得到,即F(n)=F(n-1)+F(n-2)。
当问题继续细化,即只有一级和两级台阶时,我们可以直接得出结论,无需继续细化,即F(1)=1,F(2)=2。如下图图二所示,为到达第二级台阶的走法示例。
分析到这里,该问题作为动态规划问题求解的三个要素,就全部出现了,如下所示:
边界:F(1)=1,F(2)=2。
最优子结构:F(n)的最优子结构即F(n-1)和F(n-2)。
状态转移函数:F(n)=F(n-1)+F(n-2)。
至此,爬楼梯问题的动态规划建模过程已经完成。
考虑到算法的时间和空间的效率问题,为了避免重复计算子问题的解,我们利用最优子结构特性,采用自底向上的方式进行计算,具体过程如下所述。
根据边界定义,我们已经知道前两级台阶的走法数量,如表一所示。
表一中第一行表示楼梯的台阶数量,第二行中对应的各列表示针对各级台阶可能的走法数量,在后续的求解过程中,我们可以利用表格中已知的子问题的解,根据状态转移函数,获得新的阶段的子问题的解,通过迭代,不断将表格填满,从而得到该问题的最终解。
在第一次迭代过程中,台阶数量为3,根据状态转移函数可知,F(3)=F(2)+F(1)=3,因此目前求解状态如表二所示。
在第二次迭代过程中,台阶数量为4,根据状态转移函数,可知F(4)=F(3)+F(2)=5,因此目前求解状态如表三所示。
以此类推,经过八次迭代后,即可获得第十级台阶的走法数量,如表四所示。
总结上面的过程,对每一级楼梯的走法数量而言,其求解过程所依赖的都是其前一级和前两级楼梯的走法数量。因此,在每一次迭代过程中,仅需要关注这两个变量的取值。即可获得所求的新的变量的值。
现在来把这个过程转化为程序。
定义函数upstairs(n)来计算第n级台阶的走法数量。在正式迭代之前,首先界定边界的情况。当n小于1时,说明该输入变量不合法,返回的走法数量为0;当n为1时,即为第一种边界的情况,返回值为1;当n为2时,即为第二种边界的情况,返回值为2。
之后即进入循环的迭代过程,在迭代中,根据之前的分析,仅需要关注n-1和n- 2的取值,即可通过状态转移函数F(n)=F(n-1)+F(n-2)获得F(n)的值,因此,整个迭代过程仅需要引入一个临时变量循环保存当前子问题的解即可。
由此可见,该解决方案的时间复杂度为O(n),而空间复杂度为O(1)。
二、最终代码
爬楼问题的动态规划求解的程序如下图图三所示。
首先定义边界值,确定F(1)和F(2)的取值。之后,通过函数upstairs(n)迭代计算到达各层的楼梯走法数量并输出,最终获得问题的解。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。