走楼梯问题

 

声明:题目来自: http://blog.csdn.net/v_JULY_v/archive/2010/11/17/6015165.aspx  JULY整理了100道微软等公司的面试题目,我想先不看答案:http://blog.csdn.net/v_JULY_v/archive/2011/01/10/6126406.aspx 自己先做一遍。

 

27. 跳台阶问题

题目:一个台阶总共有n级,如果一次可以跳1级,也可以跳2级。

求总共有多少总跳法,并分析算法的时间复杂度。

这道题最近经常出现,包括MicroStrategy等比较重视算法的公司都

曾先后选用过个这道题作为面试题或者笔试题。 

 

思路:

一开始我想到的方法就是排列组合,假设n是4,那可以跳1次2级,或者2次2级。跳1次2级,可以有3个位置(0代表地面,1代表第一个台阶,4代表第四个台阶,所以 这3个可以跳2级的位置分别是 0,1,2),可以写成C31 ,表示从3个位置里面选一个位置。跳2次2级的起跳位置呢?0,1,2都可以,那是不是从3个位置中选两个呢,这样的话C32 的话是3,但是实际上4个台阶跳2个2次,只有一种跳法,起跳位置是0和2。我们不能选起跳位置为1的台阶。其实我一开始从“选择合适的起跳位置的”想法就不好,因为并不是所有的位置都适合起跳的,在上面4个台阶跳2个2级的情况下,如果有一个2级是从台阶1开始起跳的话,就没有办法再放进去一个2级的跳动了。

我的一个同事拿到这个问题后,一开始的想法就是借鉴 dynamic programming的思路,就是试图 把一个大问题拆成递归式的小问题,具体来说:设F(n)表示n个台阶一共有F(n)种跳法,那F(n)和F(n-1),F(n-2), 是什么关系? 这里只是借鉴了这样的思路,并不完全是Dynamic Progamming, dynamic programming需要满足如果如果F(n)是最优的,并且F(n)可以拆成 F(k)和F(n-k),那么 F(k)和F(n-k)必须也是最优的。这里我们并不关系“最优”。

在这个跳楼梯的题目中,每次可以跳1个台阶或者2个台阶。我们考虑,我们是如何跳到N这个台阶的,一共有两种方式,一种是从N-1台阶跳一个台阶,另外一种是从 N-2跳2个台阶,所以:

F(n) = F(n-1) + F(n-2)  并且  F(1)=1, F(2) = 2 。

容易看出,其实这就是一个斐波那契数列。

后续思考:

其实这种解题的思路 和 小学奥数里面一个叫做“加法原理”的思路如出一辙。假设有一个5×4的格子M,有5行,4列。问从左下角(座标M(1,1)  )走到右上角(座标 M(5,4)  )一共有几种走法(每次移动只能从左到右,或者从下到上)。

按照题目的要求,到达M(5,4)一共有两种方法,途径M(5,3)向右移动一格,或者途径M(4,4)向上移动一格,所以,如果用F(x,y)表示从M(1,1)到达M(x,y)的路径总数,那么:

F(x,y) = F(x, y-1) + F(x-1, y) ,  F(1,1) = 0, F(1,2) = 1, F(2,1) = 1

 

总结:

清华大学黄高峯曾经写过一篇总结,我把它贴在这里:http://www.topschool.org/hx/hx/200903/1435.html 

清华大学黄高峯:谋划解题策略的几种典型方法

  对于某一个具体的问题,如何思考分析,从而谋划策略,是十分重要的。策略的谋划过程是一个思维发散的过程。问题本身千变万化,解决问题的策略也比较多,谋划策略的方法不一而足,根据人们的思维方式,我们论述以下几种谋划策略的思想。

 
  1、降格思想:从对问题的特殊和简单状态的分析中归纳出问题的实质内涵或规律,从而得到问题的一般解法,也就是我们常说的”投石问路”或者叫做”尝试归纳”。这种通过特殊求得一般、通过实际求得抽象的思想在谋划策略的过程中是十分有效的,特别是当题目中所给的数据比较大或者很抽象而难以入手时,常用这种思想来谋划策略。

  问题一:(台阶问题)一个人登上一个10级的台阶,可以一步登1级,也可以一步登2级。问:一共有几种登法?

  这道题中的台阶数为10,虽然不是很大,但一时也很难入手。我们运用降格思想分析这个问题,先看几种简单的情形:
A、仅有1级台阶时,登法只有1种:一步1级。
B、有2级台阶时,登法有2种:一步2级;1级+1级(两步)。
C、有3级台阶时,登法有3种:1级+1级+1级;1级+2级;2级+1级。
  这里”1级+2级”表示先登1级再登2级;”2级+1级”表示先登2级再登1级,显然这两种登法是不同的。
D、有4级台阶时,登法有5种:
  1级+1级+1级+1级;
  1级+1级+2级;
  1级+2级+1级;
  2级+1级+1级;
  2级+2级。
E、有5级台阶时,登法有8种:
  1级+1级+1级+1级+1级;
  1级+1级+1级+2级;
  1级+1级+2级+1级;
  1级+2级+1级+1级;
  1级+2级+2级;
  2级+1级+1级+1级;
  2级+1级+2级;
  2级+2级+1级。
  按照台阶数递增的次序把登法的种数排列如下:
1,2,3,5,8……
  容易想到这是菲波那契数列的一部分,这就找到了问题的规律,同时也谋划出解决问题的策略:数学模型(规律)策略。容易得到后继数据是:13,21,34,55,89……,于是得到问题的解:10级台阶共有89种登法。
  当然,关于找到的规律的正确性必须给出详细的证明,这里不再详述。应用这个例子无非是为了说明从简单出发考虑问题的优越性,同时指出,敏锐的洞察已有数据的能力对于谋划策略也是相当重要的。

  2、升格思想:这种思想与第一种思想恰恰相反,它是把一些过于具体的问题抽象化,目的是忽略其中的一些次要因素,从而更好地把握其中的主要因素,通过解决一般问题从而解决具体问题。有时会发生这样的情况:由于问题中给出的数据过于具体,往往会形成一种思维定势,一直针对具体的数据进行分析,而忽视了问题中显而易见的东西。运用这一思想有利于突破思维定势,更快地找到解决问题的策略,其关键是在于对抽象问题的分析和推理。

  我们把”台阶问题”抽象化,设有n级台阶,登台阶规则不变。对于登n级台阶的最后一步有两种情况:
(1)由第n-1级台阶跨一步(1级)到达第n级;
(2)由第n-2级台阶跨一步(2级)到达第n级。
  显然,以上的两类登法是不同的,n级台阶的总登法就是这两类登法的总和。我们用F(x)表示x级台阶的登法种数,则有: F(n)=F(n-1)+F(n-2)以及F(1)=1;F(2)=2。
这就迅速地得到了问题的数学模型,也就谋划出解决问题的策略:数学模型(规律)策略。
  从这个例子的分析中可以看出,对问题的抽象概括,寻求问题的一般解法,对于某些问题的解决是十分简便的。适当运用升格思想往往可以”一箭中的”,迅速描述出问题的本质,从而得到解决问题的策略。

  3、分格思想:把整个问题划分为几个相联系的子问题或几个连续的解题步骤,再一一设法解决,最后综合各个部分的解就可以得到整个问题的解。作为一种思维方法,在分析问题的时候恰当运用,对于谋划解题策略是很有效的。划分问题的方法有很多种,最基本的原则是把难以描述的问题化为易于描述的问题,把不易求解的化为易求解的。
  仍是看上面的”台阶问题”,由登10级台阶所用的步数不同,可以划分为如下的几种情况:
A、共要登10步(全部都是每步登1级):有1种登法;
B、共要登9步(只有某一步登2级,其余每步登1级):有C91=9种登法;
C、共要登8步(只有某两步每步登2级):有C82=28种登法;
D、共要登7步(只有某三步每步登2级):有C73=35种登法;
E、共要登6步(只有某四步每步登2级):有C64=15种登法;
F、共要登5步(全部都是每步登2级):有C55=1种登法;
因此总共的登法有1+9+28+35+15+1=89种。

  通过上面的对问题划分的过程以及计算的结果的分析,我们可以得到以下的两种策略:
(1)分治策略,直接仿照上面的划分过程由计算机加以实现。
(2)数学模型(规律)策略,从中归纳出:对于n级台阶的登法有几 种。
  通过上面的例子可以看出:分格思想关键在一个”分”字,也就是如何划分问题,划分中应注意两点:
1、划分必须根据统一的标准,不重复,不遗漏;
2、划分必须具有启发性,即对于谋划问题的策略有一定的启发作用。

  4、变格思想:通过转换问题某些信息,从而转化问题的形式,达到化显为隐、化繁为简、化难为易、化未知为已知的目的,通过解决等效问题来解决原问题。
  再看上面的”台阶问题”,我们运用变格思想对问题进行转化,考虑人在台阶上的位置变化,比如:人处于第1台阶上时,若一步登1级就可以到达第2级台阶;若一步登2级就可以到达第3级台阶。我们用点表示每一级台阶,用边表示台阶之间可以”一步到达”的关系,就可以得到下面的图:

《走楼梯问题》
    
  于是,问题就转化为:在图6中,求从点0到点10的所有路径总数。这是一个为我们所熟悉的图论问题,可以用穷举策略解决,当然这种策略相对于前面所谋划的那些策略而言没有什么优势,但毕竟也是解决问题的策略之一。
  如果说变格思想在上面的这个例子中的运用相对于其它思想没有什么优势的话,那么回想一下在解决”最佳航空路线问题”时,我们把它转化为”最短路径问题”、”最小费用流问题”就都是变格思想成功运用的例子了。
  运用变格思想的关键就是要”变”得巧妙,这种思想对于探索新问题,特别是未知问题是很有效的。
  以上所说的这些策略的具体算法在计算机上都是很容易实现的,具体的程序就不再给出了。

 

————————————–

PS:

当然,也可以brute-force递归的,思路是这样的:

void tryJump(int currentStairIndex)

{

      if(currentStairIndex-1 == 0) g_totalCnt++;

      if(currentStairIndex-2 == 0) g_totalCnt++;

      if(currentStairIndex-1 > 0) tryJump(currentStair-1);

      if(currentStairIndex-2 > 0) tryJump(currentStair-2);

}

tryJump(10)  –> 你会得到g_totalCnt=89, 但是我不推荐这样的做法。

点赞