回溯算法(八皇后问题)
寫在前面:博主是一位普普通通的19屆雙非軟工在讀生,平時(shí)最大的愛好就是聽聽歌,逛逛B站。博主很喜歡的一句話花開堪折直須折,莫待無花空折枝:博主的理解是頭一次為人,就應(yīng)該做自己想做的事,做自己不后悔的事,做自己以后不會(huì)留有遺憾的事,做自己覺得有意義的事,不浪費(fèi)這大好的青春年華。博主寫博客目的是記錄所學(xué)到的知識(shí)并方便自己復(fù)習(xí),在記錄知識(shí)的同時(shí)獲得部分瀏覽量,得到更多人的認(rèn)可,滿足小小的成就感,同時(shí)在寫博客的途中結(jié)交更多志同道合的朋友,讓自己在技術(shù)的路上并不孤單。
目錄:
1.回溯算法簡介
2.回溯和遞歸
3.回溯法和樹的遍歷
4.回溯法解決八皇后問題
1.回溯算法簡介
解決問題時(shí),每進(jìn)行一步,都是抱著試試看的態(tài)度,如果發(fā)現(xiàn)當(dāng)前選擇并不是最好的,或者這么走下去肯定達(dá)不到目標(biāo),立刻做回退操作重新選擇。這種走不通就回退再走的方法就是回溯法。
在解決列舉集合 {1,2,3} 中所有子集的問題中,就可以使用回溯法。從集合的開頭元素開始,對(duì)每個(gè)元素都有兩種選擇:取還是舍。當(dāng)確定了一個(gè)元素的取舍之后,再進(jìn)行下一個(gè)元素,直到集合最后一個(gè)元素。其中的每個(gè)操作都可以看作是一次嘗試,每次嘗試都可以得出一個(gè)結(jié)果。將得到的結(jié)果綜合起來,就是集合的所有子集。
實(shí)現(xiàn)代碼:
#include <stdio.h> //設(shè)置一個(gè)數(shù)組,數(shù)組的下標(biāo)表示集合中的元素,所以數(shù)組只用下標(biāo)為1,2,3的空間 int set[5]; //i代表數(shù)組下標(biāo),n表示集合中最大的元素值 void PowerSet(int i,int n){//當(dāng)i>n時(shí),說明集合中所有的元素都做了選擇,開始判斷if (i>n) {for (int j=1; j<=n; j++) {//如果樹組中存放的是 1,說明在當(dāng)初嘗試時(shí),選擇取該元素,即對(duì)應(yīng)的數(shù)組下標(biāo),所以,可以輸出if (set[j]==1) {printf("%d ",j);}}printf("\n");}else{//如果選擇要該元素,對(duì)應(yīng)的數(shù)組單元中賦值為1;反之,賦值為0。然后繼續(xù)向下探索set[i]=1;PowerSet(i+1, n);set[i]=0;PowerSet(i+1, n);} } int main() {int n=3;for (int i=0; i<5; i++) {set[i]=0;}PowerSet(1, n);return 0; } 運(yùn)行結(jié)果: 1 2 3 1 2 1 3 1 2 3 2 32.回溯和遞歸
很多人認(rèn)為回溯和遞歸是一樣的,其實(shí)不然。在回溯法中可以看到有遞歸的身影,但是兩者是有區(qū)別的。回溯法從問題本身出發(fā),尋找可能實(shí)現(xiàn)的所有情況。和窮舉法的思想相近,不同在于窮舉法是將所有的情況都列舉出來以后再一一篩選,而回溯法在列舉過程如果發(fā)現(xiàn)當(dāng)前情況根本不可能存在,就停止后續(xù)的所有工作,返回上一步進(jìn)行新的嘗試。
遞歸是從問題的結(jié)果出發(fā),例如求 n!,要想知道 n!的結(jié)果,就需要知道 n*(n-1)! 的結(jié)果,而要想知道 (n-1)! 結(jié)果,就需要提前知道 (n-1)*(n-2)!。這樣不斷地向自己提問,不斷地調(diào)用自己的思想就是遞歸。
回溯和遞歸唯一的聯(lián)系就是,回溯法可以用遞歸思想實(shí)現(xiàn)。
3.回溯法與樹的遍歷
使用回溯法解決問題的過程,實(shí)際上是建立一棵“狀態(tài)樹”的過程。例如,在解決列舉集合{1,2,3}所有子集的問題中,對(duì)于每個(gè)元素,都有兩種狀態(tài),取還是舍,所以構(gòu)建的狀態(tài)樹為:
回溯法的求解過程實(shí)質(zhì)上是先序遍歷“狀態(tài)樹”的過程。樹中每一個(gè)葉子結(jié)點(diǎn),都有可能是問題的答案。圖 1 中的狀態(tài)樹是滿二叉樹,得到的葉子結(jié)點(diǎn)全部都是問題的解。
在某些情況下,回溯法解決問題的過程中創(chuàng)建的狀態(tài)樹并不都是滿二叉樹,因?yàn)樵谠囂降倪^程中,有時(shí)會(huì)發(fā)現(xiàn)此種情況下,再往下進(jìn)行沒有意義,所以會(huì)放棄這條死路,回溯到上一步。在樹中的體現(xiàn),就是在樹的最后一層不是滿的,即不是滿二叉樹,需要自己判斷哪些葉子結(jié)點(diǎn)代表的是正確的結(jié)果。
4.回溯法解決八皇后問題
八皇后問題是以國際象棋為背景的問題:有八個(gè)皇后(可以當(dāng)成八個(gè)棋子),如何在 8*8 的棋盤中放置八個(gè)皇后,使得任意兩個(gè)皇后都不在同一條橫線、縱線或者斜線上。
八皇后問題是使用回溯法解決的典型案例。算法的解決思路是:
從棋盤的第一行開始,從第一個(gè)位置開始,依次判斷當(dāng)前位置是否能夠放置皇后,判斷的依據(jù)為:同該行之前的所有行中皇后的所在位置進(jìn)行比較,如果在同一列,或者在同一條斜線上(斜線有兩條,為正方形的兩個(gè)對(duì)角線),都不符合要求,繼續(xù)檢驗(yàn)后序的位置。如果該行所有位置都不符合要求,則回溯到前一行,改變皇后的位置,繼續(xù)試探。如果試探到最后一行,所有皇后擺放完畢,則直接打印出 8*8 的棋盤。最后一定要記得將棋盤恢復(fù)原樣,避免影響下一次擺放。
我們美麗的八皇后問題有92種排法,臥槽,沒想到吧。我也沒想到
總結(jié)
以上是生活随笔為你收集整理的回溯算法(八皇后问题)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最小生成树(普里姆算法【Prim】与克鲁
- 下一篇: 深度、广度优先生成树(C完整代码)