當把問題分成若干步驟並遞迴求解時,如果當前步驟沒有合法選擇,則函 數將返回上一級遞迴呼叫,這種現象稱為回溯。正是因為這個原因,遞迴列舉演算法常被稱為 回溯法,應用十分普遍。
在棋盤上放置8個皇后,使得它們互不攻擊,此時每個皇后的攻擊範圍為同行同列和同 對角線,要求找出所有解。
最簡單的思路是把問題轉化為「從64個格仔中選乙個子集」,使得「子集中恰好有8個格 子,且任意兩個選出的格仔都不在同一行、同
一列或同乙個對角線上」。這正是子集列舉問 題。然而,64個格仔的子集有264個,太大了,這並不是乙個很好的模型。
第二個思路是把問題轉化為「從64個格仔中選8個格仔」,這是組合生成問題。根據組合 數學,有 種方案,比第一種方案優秀,但仍然不夠好。
經過思考,不難發現以下事實:恰好每行每列各放置乙個皇后。如果用record[x]表示第x行 皇后的列編號,則問題變成了全排列生成問題。而0~7的排列一共只有8!=40320個,列舉量 不會超過它。
注意:在解決遞迴問題時,我們有時需要深入分析問題,對遞迴模型精雕細琢。一般還應 對解答樹的結點數有乙個粗略的估計,作為評價模型的重要依據,如圖7-5所示。
圖7-5中給出了四皇后問題的完整解答樹。它只有17個結點,比4!=24小。為什麼會這樣 呢?這是因為有些結點無法繼續擴充套件。例
如,在(0,2,*,*)中,第2行無論將皇后放到**,都 會和第0行和第1行中已放好的皇后發生衝突,其他還未放置的皇后更是如此。
在這種情況下,遞迴函式將不再遞迴呼叫它自身,而是返回上一層呼叫,這種現象稱為 回溯(backtracking)。
#include using namespace std;
const int maxn = 1000;
bool vis[2][maxn];
int record[maxn][maxn];
int tot, n;
void search(int cur)
} }
}int main()
cout << "-------------------------------------" << endl;
}return 0;
}
上面的程式有個極其關鍵的地方:vis陣列的使用。vis陣列的確切含義是什麼?它表示 已經放置的皇后佔據了哪些列、主對角線
和副對角線。將來放置的皇后不應該修改這些值——至少「看上去沒有修改」。一般地,如果在回溯法中修改了輔助的全域性變數,則一定要及 時把它們恢復原狀(除非故意保留所做修改)。若不信,可以把「vis[0][i]= vis[1][cur+i] = vis[2][cur-i+n] = 0」注釋掉,
驗證還能否正確求解八皇后問題。另外,在呼叫之前一定要把 vis陣列清空。
如果在回溯法中使用了輔助的全域性變數,則一定要及時把它們恢復原狀。 特別地,若函式有多個出口,則需在每個出口處恢復被修改的值。
抄自紫書
八皇后問題(回溯法)
問題描述 八皇后問題是十九世紀著名數學家高斯於1850年提出的。問題是 在8 8的棋盤上擺放8個皇后,使其不能互相攻擊,即任意的兩個皇后不能處在同意行,同一列,或同意斜線上。可以把八皇后問題拓展為n皇后問題,即在n n的棋盤上擺放n個皇后,使其任意兩個皇后都不能處於同一行 同一列或同一斜線上。問題分...
回溯法 八皇后問題
八皇后問題是高斯於1850年提出的,這是乙個典型的回溯演算法的問題。八皇后問題的大意如下 西洋棋的棋盤有8 行 8 列共64個單元格,在棋盤上擺放八個皇后,使其不能互相攻擊,也就 是說任意兩個皇后都不能處於同一行 同一列或同一斜線上。問總共有多少種擺放方法,每一種擺 放方式是怎樣的。首先來分析八皇后...
八皇后問題 回溯法
在8 8格的西洋棋上擺放八個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行 同一列或同一斜線上,問有多少種擺法 就拿四皇后來說吧 我們首先需要建立乙個一維陣列 這個陣列裡存放的就是皇后在該列合適的位置 這個陣列存放的是皇后放的行數,我們首先在第一列中找乙個可以放的地方,很明顯第乙個位置就可以...