回想一下,又鴿了一個月呢,今天高產一波。
看到“遞推”這個名詞,想必大家有一種親切又陌生的感覺吧,這個詞起源於數學,說的其實是一種數學思想。指的是后面一種狀態與前面的狀態之間的關系,例如一道小學奧數題
一,斐波那契數列
斐波那契數列又名兔子數列,因為他的初始題目就是計算兔子的。假設一對剛出生的小兔一個月后就能長成大兔,再過一個月就能生下一對小兔,並且此后每個月都生一對小兔,一年內沒有發生死亡,那么一對剛出生的兔子,在一年內繁殖成多少對兔子?
第一個月兩只小兔子,有一對兔子;
第二個月小兔子長大了,但還沒生出新的兔子,有一對兔子;
第三個月兩個大兔子生了兩只小兔子,有兩對兔子了;
第四個月 兩只小兔子長大,但還沒生小兔子,兩只大兔子又產了兩只小兔子,有三對兔子;
第五個月兩對大兔子生了兩對小兔子,那對小兔子長大了,還沒生兔子,一共是五隊兔子;
以此類推,經過計算,每個月的兔子對數分別是1,1,2,3,5,8,13,21,34,55,89,144,所以一年內繁殖成了144對兔子!
不得不說這一串數字很有點意思啊,經過觀察,從第三位“2”開始,每個數,都是前兩個數之和。
這就是斐波那契數列的規律,f(n)=f(n-1)+f(n-2)。
f(n)=f(n-1)+f(n-2)就是斐波那契數列的遞推公式。
參考一下求斐波那契數列前50位的代碼。
#include<bits/stdc++.h> using namespace std; long long a[105]; int main() { a[1]=1;a[2]=1; //前兩位是1 1 cout<<a[1]<<" "<<a[2]<<" "; for(int i=3;i<=50;i++) { a[i]=a[i-1]+a[i-2];//遞推公式 cout<<a[i]<<" "; } return 0; }
結果:
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465 14930352
24157817 39088169 63245986 102334155 165580141 267914296 433494437 701408733 1134903170 1836311903 2971215073 4807526976 7778742049 12586269025
再來一道簡單的遞推題目:
有n級台階,一次可以上兩級或者一級,問有幾種上到第n級台階有幾種方法?
可以先自行思考下……
這道題其實做法不唯一,用深度優先搜索(https://www.cnblogs.com/qj-Network-Box/p/13910876.html)可以做,不過這不是今天的主題,我也不加贅述了。
學過遞推后當然要用遞推做了
首先我們考慮一下,要想到達第n級台階,就得先到達第n-1級台階或者第n-2級台階(可以一次跨兩級或一級台階),自然到達第n級台階的方法數就是到達第n-1級台階和第n-2級台階的方法數之和。
即f(n)=f(n-1)+f(n-2)
一看,這可不是斐波那契數列嗎?唯一不同的是,這個數列開頭的兩個數是1和2。
同上,利用這個遞推公式,我們可以輕易地求出答案,代碼如下。
#include<bits/stdc++.h> using namespace std; long long a[105]; int n; int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); cin>>n; a[1]=1;a[2]=2;// 開頭兩個數 for(int i=3;i<=n;i++) { a[i]=a[i-1]+a[i-2];//遞推公式 } cout<<a[n]; return 0; }
二,遞推進階:漢諾塔問題
漢諾塔(Tower of Hanoi),又稱河內塔,是一個源於印度古老傳說的益智玩具。大梵天創造世界的時候做了三根金剛石柱子,在一根柱子上從下往上按照大小順序摞着64片黃金圓盤。大梵天命令婆羅門把圓盤從下面開始按大小順序重新擺放在另一根柱子上。並且規定,在小圓盤上不能放大圓盤,在三根柱子之間一次只能移動一個圓盤。
可以先動手試試這個漢諾塔小游戲,體驗一下—— http://www.7k7k.com/swf/201271.htm
接下來的問題就是我們如果要把n個圓盤全部移到另一個柱子上至少需要多少步?
應該還是有不少人看到過用遞歸做的漢諾塔,而這里提供用遞推的方法。事實上,在部分情況下遞推和遞歸是可以互換的。
根據上面兩道例子我們應該可以看出遞推的精髓在於找出遞推公式,那么此題的遞推公式該如何尋找呢。
按照漢諾塔問題的規則,由於大的圓盤在下,小的圓盤在上,所以在我們的目標桿上一定是先把最大的圓盤挪上去才可能完成整套成功的操作。
即要將第二大的圓盤到最小的圓盤全部移到一根桿上,將最大圓盤挪進目標桿,然后將其他圓盤一點一點挪進目標桿。
即f(n)=f(n-1)+1+f(n-1)
得出遞推公式 f(n)=2*f(n-1)+1
代碼如下
#include<bits/stdc++.h> using namespace std; long long a[105]; int n; int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); cin>>n; a[1]=1;// 只有一個圓盤的情況 for(int i=2;i<=n;i++) { a[i]=2*a[i-1]+1;//遞推公式 } cout<<a[n]; return 0; }
是不是簡潔輕松明了)))
三,遞推高階:Catalan數
這個卡特蘭數可非同一般,前幾個例子看到卡特蘭數簡直就是小巫見大巫了。
題面如下:給出一個n邊形,使用n-3條對角線,求將這個n邊形分成n-2個三角形的方法數。
這道題也是個純遞推題目,找到遞推公式就找到了突破口,代碼什么的非常好寫,但遞推公式着實難以下手;
我們先設答案為f(n),按照一定的順序給n邊形的定點標號——V1,V2,V3,V4……Vn(順時針或逆時針),由於最后要將它分成多個三角形,所以我們可以保證v1vn最后都會屬於一個△V1VnVk的某條邊,那么我們就按照k的位置分類好了。
稍加思考即可得出,三教鄉V1VnVk的左邊是一個k邊形,右邊是個n-k+1邊形,根據乘法原理可以得出包含V1VnVk的方案數為f(k)*f(n-k+1)
根據加法原理,f(n)=f(2)*f(n-1)+f(3)*f*(n-2)+……+f(n-1)*f(2),出現了,遞推公式!!!
當然,方法不唯一,我們也可以從由V1發出的對角線為切入點,對角線V1Vk把n邊形分成兩部分,根據乘法原理,包含對角線V1Vk的多邊形有f(k)*f(n-k+2)個。根據對稱性,考慮從V2V3一直到Vn出發的對角線也會有同樣的結果,因此一共有n*(f(3)f(n-1)+f(4)f(n-2)+……+f(n-1)f(3))個部分;
由於同一部分會被重復計算多次,我們需要找到他們是如何重復的
每個方案會被計算2n-6次,有n-3條對角線,考慮到每條對角線的,每個端點時均被計算了一次。
這樣就能夠得到f(n)的遞推式f(n)=(f(3)(n-1)+f(4)f(n-2)+……+f(n-1)f(3))*n/(2n-6)
經過推算得f(n+1)=((4n-6)/n)*f(n);求出了遞推公式;
卡特蘭數的代碼實現就交給大家了,難度不算大,但對於遞推公式如何推出來的一定要多揣摩揣摩。
好了如果您讀了我的文章覺得還不錯,請不要吝惜手中的贊和關注,有問題可以在評論區留言哦