一隻青蛙一次能夠跳上1級臺階,也能夠跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法(前後次序不一樣算不一樣的結果)。html
首先對這道題,咱們能夠經過找規律來解
一隻青蛙能夠跳上1級臺階,也能夠跳上2兩級臺階
當n = 1時,有1種跳法
當n = 2時,有2種跳法
當n = 3時,有3種跳法
當n = 4時,有5種跳法
當n = 5時,有8種跳法
...
等等,1,2,3,5,8...,多麼熟悉的數列,斐波那契?
仔細想一想當有n(n >= 2)級臺階時,求F(n)
青蛙第一步能夠選擇跳上1級臺階,則還剩n - 1級臺階須要跳,即F(n - 1)
青蛙第一步也能夠選擇跳上2級臺階,則還剩n - 2級臺階須要跳,即F(n - 2)
則總的跳法F(n) = F(n - 1) + F(n - 2),毫無疑問這就是斐波那契數列的定義了。
關於斐波那契的求解方法,讀者能夠參考【劍指Offer】斐波那契數列,包括了遞歸,動態規範,矩陣快速冪多種解法,這裏就再也不贅述了。
下面放上以求解斐波那契數列的方式解題的其中一種寫法。算法
public int jumpFloor(int number) { int f1 = 0; int f2 = 1; while (number-- > 0) { f2 = f1 + f2; f1 = f2 - f1; } return f2; }
網上大多數的解法都是以求解斐波那契數列的思路來解題,但其實使用排列組合的思想也能夠求解(真相是一開始沒發現是斐波那契,纔想到排列組合的-_-||)。
解題前先簡單介紹下排列組合,摘自百度百科c#
排列
。從n個不一樣元素中取出m個元素的全部排列的個數,叫作從n個不一樣元素中取出m個元素的排列數,用符號A(n,m)表示。組合
。從n個不一樣元素中取出m個元素的全部組合的個數,叫作從n個不一樣元素中取出m個元素的組合數。用符號C(n,m) 表示。回到本題,青蛙能夠跳上1級臺階,也能夠跳上2級臺階,
對於n級臺階來講,它最多能夠跳 n/2 次 2 級臺階,也就是說總的跳法數是跳0次2級臺階跳法數,1次2級臺階跳法數,2次2級臺階跳法數 ... n/2 次2級臺階跳法數的總和
如今問題就變成了求跳指定次數2級臺階的跳法數
假設有n級臺階,指定要跳m次2級臺階,還剩下n - 2m個1級臺階,則青蛙一共要跳n - 2m + m = n - m次才能跳完。即在n-m次跳躍中選擇m次跳2級臺階,有多少種跳法數呢,C(n - m,m)種。(注意這裏不是An - m,m),由於選出的m次2級臺階沒有順序性)
因此咱們求出C(n - 1,1),C(n - 2,2),C(n - 3,3) .. C(n - n/2, n/2),而後將其相加,記得再加上1(選0個2級臺階,1種跳法),就是總的跳法數
實現代碼時注意求組合數C(n,m)的算法,須要作一些優化,主要有兩點
先化簡公式
\[C{^m_n} =\frac{A{^m_n}}{m!} =\frac{n * (n - 1) * (n-2) * ... * (n-m+1)}{m!} \]
1,若是C(n,m)的結果用c#的int類型存儲,則最大值爲2^31^-1,若是按照公式依次計算分子和分母,可能分子或分母會超過2^31^-1,從而形成異常。因此優化爲分子和分母的計算同步進行,當判斷分子除以分母能夠除整的時候,就先相除,避免累計數過大。後面的代碼會有對應註釋。
2,能夠省略的計算就省略
好比求解C(5,4),其中的4 * 3 * 2,分子分母能夠直接相約,原本分子分母的計算都須要連乘4次,如今只須要連乘1次
\[C{^4_5} = \frac{5 * 4 * 3 * 2}{4 * 3 * 2 * 1} \]優化
// 求解cnm public int c(int n, int m) { int count = m; int nf = 1, mf = 1; if (m >= n - m + 1) { // 對應優化2,只須要連乘n-m次 count = n - m; } for (int i =0; i < count; i++) { nf *= (n - i); mf *= (i + 1); // 對應優化1,能夠除整的時候就先除 if (nf % mf == 0) { nf = nf / mf; mf = 1; } } return nf / mf; } public int jumpFloor(int number) { int ret = 1; int count = number / 2; for (int i = 1; i <= count; i++) { ret += c(number - i, i); } return ret; }