DFS与N皇后问题
DFS與N皇后問題
DFS
什么是DFS
DFS是指深度優(yōu)先遍歷也叫深度優(yōu)先搜索。
它是一種用來遍歷或搜索樹和圖數(shù)據(jù)結(jié)構(gòu)的算法
注:關(guān)于樹的一些知識(shí)可以去看《樹的概念及基本術(shù)語》這篇文章
它會(huì)不斷地沿著節(jié)點(diǎn)的深度方向(該深度方向?yàn)槠溧徑狱c(diǎn)的方向)進(jìn)行遍歷
DFS如何實(shí)現(xiàn)
DFS主要步驟有以下幾步
- 訪問并從某節(jié)點(diǎn)
- 向鄰接點(diǎn)出發(fā),訪問路徑向深處走
- 若走到最深處還有節(jié)點(diǎn)沒訪問,則再回到該層訪問該節(jié)點(diǎn)(回溯)
- 依次重復(fù)上述步驟,直至所有路徑都被訪問(遞歸)
DFS時(shí)空復(fù)雜度
空間復(fù)雜度
DFS算法實(shí)際上是一個(gè)遞歸算法,需要借助一個(gè)遞歸工作棧
故
- 空間復(fù)雜度:O(n)
時(shí)間復(fù)雜度
- 時(shí)間復(fù)雜度:不確定
為什么不確定?
因?yàn)楸闅v圖的過程實(shí)質(zhì)上是對(duì)每個(gè)頂點(diǎn)找查其所有鄰接點(diǎn)的過程,其耗費(fèi)時(shí)間取決于所采用結(jié)構(gòu)
下面給出常用的幾種數(shù)據(jù)結(jié)構(gòu)DFS時(shí)的時(shí)間復(fù)雜度
(注:n是圖中結(jié)點(diǎn)的個(gè)數(shù),e是圖中邊的個(gè)數(shù)。)
- 鄰接表 O(e+n)
- 鄰接矩陣 O(n^2)
N皇后問題
N皇后問題(HDU2553)
HDU 2553 N皇后問題在N*N的方格棋盤放置了N個(gè)皇后,使得它們不相互攻擊(即任意2個(gè)皇后不允許處在同一排,同一列,也不允許處在與棋盤邊框成45角的斜線上。 你的任務(wù)是,對(duì)于給定的N,求出有多少種合法的放置方法。輸入:共有若干行,每行一個(gè)正整數(shù)N≤10,表示棋盤和皇后的數(shù)量;如果N=0,表示結(jié)束。輸出:共有若干行,每行一個(gè)正整數(shù),表示對(duì)應(yīng)輸入行的皇后的不同放置數(shù)量。輸入樣例: 1 8 5 0輸出樣例: 1 92 10經(jīng)典DFS帶來的問題
而經(jīng)典的DFS,基本是將所有子節(jié)點(diǎn)全部擴(kuò)展開,再選取最新的節(jié)點(diǎn)進(jìn)行擴(kuò)展。
但是對(duì)于N皇后來說,我們?nèi)绻灰幻杜e,則有N的N次方種情況.
即使N=10,則有10^10種情況,我們還要在這些情況中進(jìn)行篩選,這無疑需要巨大的計(jì)算
故:我們需要對(duì)DFS進(jìn)行優(yōu)化
回溯與減枝
我們可以讓程序在某節(jié)點(diǎn)由某些條件就可以判斷再進(jìn)行繼續(xù)展開并不符合要求,然后立即返回,達(dá)到回溯和減少樹的枝干的生成(減枝)的目的,從而進(jìn)行優(yōu)化。
下面給出N=4時(shí)減枝的樹狀圖解
問題分析
關(guān)鍵問題
關(guān)鍵問題:在擴(kuò)展節(jié)點(diǎn)時(shí)如何去掉不符合條件的節(jié)點(diǎn)?
設(shè)左上角是(0,0),已經(jīng)放好皇后是(i,j),不同行、列、斜線的新皇后是(r,c),則:
- 橫向不同行 i != r
- 縱向不同列 j != c
- 斜對(duì)角:從(i,j)向斜對(duì)角走a步,新坐標(biāo)(r,c)有4種情況 (i-a,j-a) (i+a,j-a) (i-a,j+a) (i+a,j+a),即|i-r| = |j-c|。也就是說不再斜線上滿足 |i-r| != |j-c|
其他問題
- 由于給出N<=10,所以可以用打表的方式,將每種n取值對(duì)應(yīng)的結(jié)果放到數(shù)組ans[n]中
- 對(duì)于數(shù)組ans[]由于大小最方便設(shè)為11,n在0–10中取值一共11種情況。盡管我們可以忽略0定義成大小為10的數(shù)組(將n=1結(jié)果存放在ans[0]以此類推),但我們?cè)谌〕鰯?shù)據(jù)時(shí)還要進(jìn)行減法運(yùn)算并不方便。
- 對(duì)于在對(duì)特定的n取值分析時(shí)可用個(gè)tot變量來記錄到達(dá)最深處情況的個(gè)數(shù),由于循環(huán)是從0開始,我們只需要讓r=n時(shí)tot自增1來記錄并回溯
- 數(shù)組col[r] = c,表示皇后放在第r行c列,我們可以用col[i] 來依次獲得前幾行皇后位置信息,故對(duì)于本題col[]數(shù)組定義最大為10就行
題解代碼
#include <iostream>using namespace std;//tot記錄每次n取特定值能放置n皇后情況的個(gè)數(shù),每當(dāng)n改變時(shí)tot重置為0 int n, tot = 0;//表示皇后存放位置 col[r] = c 表示第r行的皇后在第c列 int col[10];//r為行,c為列 //check()用于檢查新放置的皇后(r,c)是否和已經(jīng)存放好的皇后沖突 //沖突返回false,不沖突返回true bool check(int r, int c) {for (int i = 0; i < r; i++)if (col[i] == c || (abs(col[i] - c) == abs(i - r))) //絕對(duì)值判斷是否在同一斜線上return false; //沖突返回falsereturn true; //不沖突返回true }//r為檢索的行 //對(duì)每行檢索并放置皇后 void DFS(int r) {//若達(dá)到最底行(層)則新增1種情況,并返回(回溯)if (r == n) {tot++;return;}//對(duì)第r行(層)的每一列進(jìn)行檢索,若能放置則進(jìn)入下一行(層)再從r+1行第0列向后檢索放置依次類推//若不能放置則本層次結(jié)束,遞歸然后回溯出去再改變上一層的列數(shù),從該列再進(jìn)行展開for(int c=0;c<n;c++)if (check(r, c)) //檢查若放在該層該列是否與之前皇后沖突{col[r] = c; //記錄再第r行c列放皇后,用于下一層向上檢驗(yàn)沖突DFS(r + 1); //繼續(xù)放下一層皇后}}int main() {//用于打表記錄結(jié)果的數(shù)組int ans[11];memset(ans, 0, sizeof(0));//算出n取所有值的答案for (n = 0; n <= 10; n++){memset(col, 0, sizeof(col)); //清空上一回的數(shù)組,計(jì)算下一個(gè)n皇后問題tot = 0;DFS(0);ans[n] = tot; //打表,保存}while (cin >> n){if (n == 0)return 0;cout << ans[n] << endl;}return 0; }數(shù)據(jù)
下面給出n從1到10取值的數(shù)據(jù)以供參考
n取1的結(jié)果為:1 n取2的結(jié)果為:0 n取3的結(jié)果為:0 n取4的結(jié)果為:2 n取5的結(jié)果為:10 n取6的結(jié)果為:4 n取7的結(jié)果為:40 n取8的結(jié)果為:92 n取9的結(jié)果為:352 n取10的結(jié)果為:724復(fù)雜度
以下對(duì)于本題解法而言
- check()復(fù)雜度 O(n!)
- DFS()復(fù)雜度 O(N)
- 總復(fù)雜度 O(N!·N)
對(duì)于N>11且N<=15可用數(shù)據(jù)結(jié)構(gòu)舞蹈鏈較快解決
數(shù)學(xué)性質(zhì)
- N皇后問題在數(shù)學(xué)上是個(gè)NP完全問題,不存在多項(xiàng)式時(shí)間算法
本文參考
-
什么是Dfs? - 知乎 (zhihu.com)
-
DFS是什么意思?_百度知道 (baidu.com)
-
(3條消息) DFS時(shí)間復(fù)雜度_liuxiaocs7的博客-CSDN博客_dfs時(shí)間復(fù)雜度
-
dfs時(shí)間復(fù)雜度分析 - onlyblues - 博客園 (cnblogs.com)
-
(3條消息) DFS時(shí)間復(fù)雜度_liuxiaocs7的博客-CSDN博客_dfs時(shí)間復(fù)雜度
-
dfs時(shí)間復(fù)雜度分析 - onlyblues - 博客園 (cnblogs.com)
-
《算法競(jìng)賽入門到進(jìn)階》—— 清華大學(xué)出版社
總結(jié)
- 上一篇: 哈希排序算法
- 下一篇: Anaconda安装及第一个py程序