在前一篇文章中已經介紹了如何應用窮舉法來解決具體的程式設計問題。窮舉法最為常用,可以解決大部分常見的問題。今天再來介紹一種新的解題思路: 遞迴法。 它可以看成是對窮舉法的一種補充。它的思路是在不方便窮舉所有的可能時,通過設定特定的函式,在該函式內部反覆地呼叫自身並輸入不同的引數,以達到遍歷所有可能性的目的。使用遞迴法**量小,編碼簡單,邏輯清晰,不易出錯。下面通過乙個經典的例子來讓大家感受下遞迴法的應用方法。
例1: 漢諾塔問題
漢諾塔問題是最經典的只能夠使用遞迴的方法解決的問題,題目描述如下:
據傳說,在古代世界中心的貝拿勒斯(印度北部)的聖廟裡,一塊在黃銅板上插著3根寶石針。印度教的主神梵天在創造世界時,在其中的一根針上自下而上地穿好了由大至小的64層金片,即為漢諾塔。無論白天黑夜,總有乙個僧侶按如下的法則移動這些金片,一次只能夠移動一層,不管在哪根針上,小片必須在大片的上面。 要求借助於第二根針將整個漢諾塔移至第三根針上。 圖示如下:
假設漢諾塔只有4層,其具體的移動過程如下動畫所示:
本題具有如下的特點,使之不能使用窮舉法:
1. 不便於窮舉: 如果用數學的方法估算,假設一秒鐘移動一片,將64層的漢諾塔移至另一根針上所需的步驟是個天文數字:共需接近5845億年。 由於取值範圍過大,且無法迴圈,不可能將所有的過程都窮舉實現出來。
2. 可以經由一系列有限的步驟實現,其中的每個步驟都很相似,這道題為例,初始時有n層,第一大步就是將上面的n-1層移動至第三根針上,再將最下面的第n層移動至第二根針上,再將第三根針上的n-1層借助第一根針移動至第二根針上。在移動n-1層時,所使用的過程與移動n層是相同的,無非是初始針,目標針和輔助針和移動的層數不同而已。
只要滿足這樣的條件,都可以試著使用遞迴的方法來設計,過程如下:
定義函式 movehannoi(層數,初始針,輔助針,目標針)
這樣一來,只需要第一次輸入初始人層數,初始針,輔助針,目標針的引數,即可得到結果。省卻了複雜的迴圈遍歷的麻煩。
像這種在乙個函式內部又呼叫自身,只是每次呼叫時傳遞引數不同的現象,稱為遞迴。
下面再舉兩個例子來強化大家對於遞迴演算法的認識:
例2: 給定乙個正整數,輸出它的階乘。
數學中階乘的定義就是從1開始逐步累乘自然數到當前的數,使用!來表示階乘,如
1! = 1
2! = 1*2
3!=1*2*3
4! = 1*2*3*4
……很快大家就會想到使用窮舉法,確實,用窮舉法能夠非常直接地解決此問題,核心**如下(c#)
……int sum =1;
for(int i=1; i<=n; i++)
sum *= i;
輸出sum的結果
再來仔細地分析階乘的分解過程,很容易發現了如下的規律:
1! =1
2! = 2*1! = 2
3! = 3*2!=3*2*1! = 6
4! = 4*3!=4*3*2!=4*3*2*1! = 24
總結如下: 1的階乘為1,其它任何數的階乘等於n乘以(n-1)的階乘。 這裡為了求得n的階乘,必須用同樣的方法求得(n-1)的階乘,再與當前數相乘得到結果。這與漢諾塔中的遞迴過程何其相似。設計遞迴的演算法如下:
定義函式 jiecheng(int 當前數) 返回當前數的階乘
用c#語言實現如下:
int jiecheng(int curnum)
例3: 在8*8的西洋棋棋盤上,放置了8個皇后,使之不能相互攻擊,找出所有滿足條件的布局
分析:
(1) 由於西洋棋中皇后的威力最大,橫、豎、斜三個方向均可攻擊。若想8個皇后和平共處,必須保證棋盤上橫、豎、斜三個方向上最多只有乙個皇后。
(2) 8*8的棋盤可以使用8*8的二維陣列來表示,0代表空,1代表皇后,將此二維陣列輸出即可表示棋盤的狀態。
(3) 以列為單位,自左至右依次地在每一列中由上到下放置皇后,每放置乙個之後,看它是否與現有的皇后相衝突。若沒有衝突,繼續試探下一列。若有衝突,嘗試擺放到下一行。最終當試探完最後一列且無衝突之後,輸出結果。
設行號為0-7, 列號為0-7 其執行過程如下:
定義函式 place(當前列號,棋盤) //表示試探棋盤上的某一列
從第0行遍歷至第7行,每次遍歷時}
通過上面的例子,可以得出遞迴法的程式設計模式如下:
1. 嘗試著做某種最基本的操作,該操作將會改變現有的狀態
漢諾塔: 移動一層塔,導致初始針,輔助針,目標針的狀態改變。
階乘: 當前數乘以比它小一的數的階乘,改變了累乘的結果。
八皇后: 放置乙個皇后在棋盤格式裡,當前棋盤的狀態改變了。
2. 根據當前的狀態,觀察是否到了 不需要遞迴的時刻,如:
漢諾塔: 前n-1層已經移動至輔助針,直接將當前層移動至目標針,不需要遞迴。
階乘: 當前數為1時,不需要遞迴,直接返回1
八皇后:0-7列全部試探完畢,不再遞迴
3.如果未到不必遞迴時,遞迴地呼叫自身,注意每次呼叫時的引數不同:
漢諾塔: 若當前n-1層未完全移至輔助針,需要遞迴呼叫自身,使用的引數「層數」,「初始針」,「目標針」,「輔助針」都會發生變化
階乘: 當前數不為1時,遞迴呼叫階乘本身,引數要變成「當前數-1」,返回值為當前數*下一階乘數
4. 撤消當前的操作,狀態還原(可選)
八皇后問題中,在某一列上放置了皇后,無論是否合適,都要在試探完畢之後撤走,再放置到下一位置。這個撤消的操作在迷宮類的題目中非常重要,否則無法得到所有的可能狀態。但漢諾塔與階乘問題中不需要狀態還原。
作業:1. 根據上述的分析,使用c#語言實現6層漢諾塔問題的解法。假設3根針分別為a,b,c,初始時a針上自小而大疊放著6層塔片,要求輸出移動到c針上時的全部具體的路徑。
2. 求解8皇后問題。要求使用二維整型的陣列,其中0值表示空,1值表示皇后,使用0與1的矩陣來表示滿足條件的棋盤狀態,要求找出所有可能的結果。
下圖即為一種輸出狀態
00000100
01000000
00000010
10000000
00010000
00000001
00001000
00100000
藍橋杯 振興中華(遞迴法)
題目描述 小明參加了學校的趣味運動會,其中的乙個專案是 跳格仔。地上畫著一些格仔,每個格仔裡寫乙個字,如下所示 從我做起振 我做起振興 做起振興中 起振興中華 比賽時,先站在左上角的寫著 從 字的格仔裡,可以橫向或縱向跳到相鄰的格仔裡,但不能跳到對角的格仔或其它位置。一直要跳到 華 字結束。要求跳過...
藍橋杯 演算法提高 遞迴 C語言
試題 演算法提高 遞迴 資源限制 時間限制 1.0s 記憶體限制 256.0mb 問題描述 當x 1時,hermite多項式的定義見第二版教材125頁。使用者輸入x和n,試編寫 遞迴 函式,輸出對應的hermite多項式的值。其中x為float型,n為int型。輸入格式 x n輸出格式 對應多項式的...
藍橋杯 遞迴問題
深入遞推,逐級回退 遞迴問題 深搜dfs 在有條件的情況下試探各種情況 找出口 遞迴的終止條件 遞迴函式引數邊界值的界定 思想 1.src上的n 1個盤子移到medium 2.src剩下的乙個最大的盤子移到dest 3.medium上的n 1個盤子移到dest 把src最上面的乙個盤子移到dest ...