动态规划之-上台阶问题

最近在刷题,碰到了上台阶问题,据说这也是Google的面试题,今天来整理一下。

问题描述

有一楼梯共m级,若每次只能跨上一级或二级,要走上第m级,共有多少走法?

这种问题老司机一看就知道用动态规划的思想去求解,但是小白如我们可怎么办呢?

不懂动态规划?没事,不懂也可以解出来这个题目的。不要着急,且听我娓娓道来。

现在不是让我们求上到m层有多少种走法吗?好啊,设走到m层需要x种走法,这个套路大家肯定非常熟悉,中学数学最喜欢设各种东西。好,现在要开始求解x了,首先我们自然会想到先找找看这个x跟什么有关系,看能不能跟谁联立一个方程啥的不就好整了么?

思路是对的,那x跟谁有关系呢?把目光转向题目要求(问题本身会提供有用信息,这是中学数学的套路),我们看到题目说每一次只能跨一步或两步,也就是说要想到达m层,我们只能从m-1层跨一步上去或者从m-2层跨两步上去。假设到达m-1层有x1种走法,到达m-2层有x2种走法,那么:

x = x1 + x2

从上式中可以看出所求到达m层的走法x是依附于到达m-1层的走法x1和到达m-2层的走法x2。看到没有,一个问题的解依附于其子问题的解(动态规划的精髓所在)。也就是说只要我们知道了m-1层的走法x1和m-2层的走法x2就能知道到达m层的走法x了。

到这里,大家可能会问了,那x1 和 x2不也不知道吗? x不还是求不出来么?恩,是的,但是既然我们知道m层的走法能从m – 1层和m-2层的走法求得,那同样的道理,m-1层的走法不也可以从(m-1) – 1和(m-1)-2层的走法求得吗?m-2层的走法不也可以从(m-2)-1和(m-2)-2层的走法求得吗?

似不似头晕乎乎的?来整个小栗子看看:

这里我们用一个数组Box来存储各个层的走法,Box[i]表示走到第i层有多少种走法。也即是说我们上边的x,x1,x2可以用Box[m], Box[m-1], Box[m-2]来表示,这样看起来是不是更清晰了。

好了,小栗子来了,现在我给你6个台阶,每次只能跨一步或者两步,问有多少种走法到达第6层?

请看图:

《动态规划之-上台阶问题》

要到达第六层,只有两种可能:

  1. 跨一个台阶从第5层上去
  2. 跨两个台阶从第4层上去

也就是说,要么从第5层上去;要么从第4层上去。又已知到达第5层有Box[5]种上法;到达第4层有Box[4]种上法。

因此,上到第6层有 Box[5] + Box[4] 种上法,即

Box[6] = Box[5] + Box[4]

推广开来,即有:

Box[i] = Box[i – 1] + Box[i – 2], (i > 2)
当 i = 1时,Box[1] = 1;
当 i = 2时,Box[2] = 2;

为啥要加个条件 i > 2 呢?因为你想啊,如果i = 2,那就有Box[2] = Box[1] + Box[0];i = 1,即有Box[1] = Box[0] + Box[-1]。而我们存储的时候是从Box[1]开始存储的,Box[0]我们是不存东西的,Box[-1]更是会数组下标越界,所以要加个约束条件,也叫边界条件。而对于Box[1],即到达第一个台阶的走法,那只有一种可能,就是跨一步,所以Box[1] = 1; 对于Box[2],即到达第2个台阶的走法,这时你可以一次一步上,也可以一次跨两步上,所以有Box[2] = 2。

因此有,
Box[6] = Box[5] + Box[4]
=(Box[4] + Box[3]) + (Box[3] + Box[2])
=((Box[3] + Box[2]) + (Box[2] + Box[1])) + ((Box[2] + Box[1]) + Box[2])
=((Box[2] + Box[1] + Box[2]) + (Box[2] + Box[1])) + ((Box[2] + Box[1]) + Box[2])

看到没有,所求的Box[6]最后都化成了我们已知的Box[1]和Box[2]的组合了。
但是,我们的代码也要这么样去拆分求解吗?NO,NO,NO,如果是这样的话,那就还没有领略到动态规划的美妙之处,哈哈,啥意思呢?既然你的问题都可以用子问题来求解,那为什么我不把所有子问题都求解好存起来,这样你一来求解,我就可以立马返回给你你所需要的子问题的解呢?

比如现在我们已知Box[1]和Box[2],先把他们存起来,那Box[3]是不是就知道了(Box[3] = Box[2] + Box[1]),再把Box[3]存起来,同理Box[4],Box[5],Box[6]也都可以求得了,并且都把他们存在Box中,就像是金字塔一样,先从最小的子问题慢慢往上推出父问题的解,这样一来,所有问题都可以马上取出它的解了。

好了,到此问题讲述的差不多了,其实如果大家理解了这个问题,那你就在不知不觉中开始接触了动态规划了,关于动态规划更详细的介绍,我之后会整理一篇文章出来,敬请期待。

最后,附上上台阶问题的完整代码:

import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in)
        int m = scan.nextInt();
        int[] Box = new int[m + 1];//第0个位置舍弃不用,从第一个位置开始存
        Box[1] = 1;//我们已知Box[1]和Box[2],这也是边界条件
        Box[2] = 2;
        for (int j = 2; j <= m; j++) {//依次计算并存储每个问题的解
            Box[j] = Box[j - 1] + Box[j - 2];//这句是核心!
        }
        System.out.println(Box[m]);//Box[m]即走到m层的走法
    }
}
    原文作者:JxYoung
    原文地址: https://www.jianshu.com/p/0b1a3cf4442a
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞