所謂遞推,是指從已知的初始條件出發,依據某種遞推關係,逐次推出所要求的各中間結果及最後結果。其中初始條件或是問題自己已經給定,或是經過對問題的分析與化簡後肯定。ios
利用遞推算法求問題規模爲n的解的基本思想是:當n=1時,解或爲已知,或能很是方便地求得;經過採用遞推法構造算法的遞推性質,能從已求得的規模爲一、二、…、i−1的一系列解,構造出問題規模爲i的解。這樣,程序可從i=0或i=1出發,重複地由已知至i−1規模的解,經過遞推,得到規模爲i的解,直至得到規模爲n的解。算法
可用遞推算法求解的問題通常有如下兩個特色: (1) 問題能夠劃分紅多個狀態; (2) 除初始狀態外,其它各個狀態均可以用固定的遞推關係式來表示。固然,在實際問題中,大多數時候不會直接給出遞推關係式,而是須要經過分析各類狀態,找出遞推關係式。編程
利用遞推算法解決問題,須要作好如下四個方面的工做:數組
(1)肯定遞推變量spa
應用遞推算法解決問題,要根據問題的具體實際設置遞推變量。遞推變量能夠是簡單變量,也能夠是一維或多維數組。從直觀角度出發,一般採用一維數組。ci
(2)創建遞推關係io
遞推關係是指如何從變量的前一些值推出其下一個值,或從變量的後一些值推出其上一個值的公式(或關係)。遞推關係是遞推的依據,是解決遞推問題的關鍵。有些問題,其遞推關係是明確的,大多數實際問題並無現成的明確的遞推關係,需根據問題的具體實際,經過分析和推理,才能肯定問題的遞推關係。stream
(3)肯定初始(邊界)條件基礎
對所肯定的遞推變量,要根據問題最簡單情形的數據肯定遞推變量的初始(邊界)值,這是遞推的基礎。變量
(4)對遞推過程進行控制
遞推過程不能無休止地重複執行下去。遞推過程在何時結束,知足什麼條件結束,這是編寫遞推算法必須考慮的問題。
遞推過程的控制一般可分爲兩種情形:一種是所需的遞推次數是肯定的值,能夠計算出來;另外一種是所需的遞推次數沒法肯定。對於前一種狀況,能夠構建一個固定次數的循環來實現對遞推過程的控制;對於後一種狀況,須要進一步分析出用來結束遞推過程的條件。
遞推一般由循環來實現,通常在循環外肯定初始(邊界)條件,在循環中實施遞推。
遞推法從遞推方向可分爲順推與倒推。
所謂順推法是從已知條件出發,經過遞推關係逐步推算出要解決的問題的結果的方法。如求斐波拉契數列的第20項的值,設斐波拉契數列的第n項的爲f(n),已知f(1)=1,f(2)=1;經過遞推關係式f(n)=f(n-2)+f(n-1) (n>=3,n∈N),能夠順推出f(3)=f(1)+f(2)=二、f(4)=f(2)+f(3)=三、…直至要求的解f(20)=f(18)+f(19)=6765。
所謂倒推法,就是在不知初始值的狀況下,經某種遞推關係而獲知了問題的解或目標,從這個解或目標出發,採用倒推手段,一步步地倒推到這個問題的初始狀況。
一句話歸納:順推是從條件推出結果,倒推從結果推出條件。
順推法是從前日後推,從已求得的規模爲一、二、…、i−1的一系列解,推出問題規模爲i的解,直至獲得規模爲n的解。順推算法可描述爲:
for (k=1; k<=i−1; k++)
f[k]= <初始值>; // 按初始條件,肯定初始值
for (k=i; k<=n; k++)
f[k]= <遞推關係式>; // 根據遞推關係實施遞推
cout<<f[n]; // 輸出n規模的解f(n)
倒推法是從後往前推,從已求得的規模爲n、n−一、…、i+1的一系列解,推出問題規模爲i的解,直至獲得規模爲1的解(即初始狀況)。倒推算法可描述爲:
for (k=n; k>=i+1; k--)
f[k]= <初始值>; // 按初始條件,肯定初始值
for (k=i; k>=1; k--)
f[k]= <遞推關係式>; // 根據遞推關係實施遞推
cout<<f[1]; // 輸出問題的初始狀況f(1)
遞推問題通常定義一維數組來保存各項推算結果,較複雜的遞推問題還需定義二維數組。例如,當規模爲i的解爲規模爲一、二、…、i−1的解經過計算處理決定時,可利用二重循環處理這一較爲複雜的遞推。
【例1】RPG塗色問題
有排成一行的n個方格,用紅(Red)、粉(Pink)、綠(Green)三種顏色塗每一個格子,每一個格子塗一種色,要求任何相鄰的方格不能同色,且首尾兩格也不一樣色。
編寫一個程序,輸入方格數n(0<n<=30),輸出知足要求的所有塗法的種數。
(1) 編程思路
設知足要求的n個方格的塗色方法數爲F(n)。
由於RPG有三種顏色,能夠先枚舉出當方格數爲一、二、3時的塗法種數。
顯然, F(1)=3 (即R、P、G三種)
F(2)=6 (即RP、RG、PR、PG、GR、GP六種)
F(3)=6 (即RPG、RGP、PRG、PGR、GRP、GPR六種)
當方格的個數大於3時,n個方格的塗色方案能夠由n-1方格的塗色方案追加最後一個方格的塗色方案得出,分兩種狀況:
1)對於已按要求塗好顏色的n-1個方格,在F(n-1)種合法的塗色方案後追加一個方格(第n個方格),因爲合法方案的首尾顏色不一樣(即第n-1個方格的顏色不與第1個方格的相同),這樣,第n個方格的顏色也是肯定的,它一定是原n-1個方格的首尾兩種顏色以外的一種,所以,在這種狀況下的塗色方法數爲F(n-1)。
2)對於已按要求塗好顏色的n-2個方格,能夠在第n-1個方格中塗與第1個方格相同的顏色,此時因爲首尾顏色相同,這是不合法的塗色方案,但能夠在第n個方格中塗上一個合法的顏色,使其成爲方格長度爲n的合法塗色方案(注意:當n等於3時,因爲第1(3-2)個方格與第2(3-1)個方格顏色相同,第3個方格不論怎樣塗都不會合法,所以遞推的前提是n大於3),在第n個方格中能夠塗上兩種顏色(即首格外的兩種顏色,由於與它相連的第n-1個方格和第1個方格的顏色是同樣的),所以,在這種狀況下的塗色方法數爲2*F(n-2)。
由此,可得遞推公式:F(n)= F(n-1) + 2*F(n-2) (n>=4)
程序中定義3個變量f一、f2和f3分別表示F (n-2)、F(n-1)和F(n),初始時f1=六、f2=6。
當n<4時,根據初始狀況直接輸出結果。
當n>=4時,用循環遞推計算F(n)。程序段描述爲:
for(i=4;i<=n;i++)
{
f3=f1+f2; // 計算當前F(i)
f1=f2; f2=f3; // 爲下一次遞推作準備
}
(2)源程序及運行結果
#include <iostream>
using namespace std;
int main()
{
int i,n,f1,f2,f3,num;
cout<<"請輸入方格的數目 n (0<n<=30):";
cin>>n;
if (n==1) num=3;
else if (n==2 || n==3) num=6;
else
{
f1=6; f2=6;
for(i=4;i<=n;i++)
{
f3=2*f1+f2; // 遞推求F(i)
f1=f2; f2=f3; // 爲下次遞推作準備
}
num=f3;
}
cout<<n<<"個方格的正確塗色方案一共有"<<num<<"種。"<<endl;
return 0;
}
爲更清晰地描述遞推過程並保存中間結果,能夠定義一個一維數組f[31],數組元素f[i]保存總數爲i個方格的塗色方法數。初始值: f[1]=三、f[2]=六、f[3]=6。源程序清單以下。
#include <iostream>
using namespace std;
int main()
{
int i,n,f[31];
f[0]=0;
f[1]=3;
f[2]=6;
f[3]=6;
for(i=4;i<31;i++)
f[i]=f[i-1]+2*f[i-2];
cout<<"請輸入方格的數目 n (n<=30):";
cin>>n;
cout<<n<<"個方格的正確塗色方案一共有"<<f[n]<<"種。"<<endl;
return 0;
}