n皇后問題
n皇后問題是將n個皇后放置在n*n的棋盤上,皇后彼此之間不能相互攻擊。
給定乙個整數n,返回所有不同的n皇后問題的解決方案。
每個解決方案包含乙個明確的n皇后放置布局,其中「q」和「.」分別表示乙個女王和乙個空位置。
樣例對於4皇后問題存在兩種解決的方案:
[[".q..", // solution 1
"...q",
"q...",
"..q."],
["..q.", // solution 2
"q...",
"...q",
".q.."]
] n皇后問題 ii
根據n皇后問題,現在返回n皇后不同的解決方案的數量而不是具體的放置布局。
樣例比如n=4,存在2種解決方案
n皇后問題是乙個經典的問題,在乙個n*n的棋盤上放置n個皇后,每行乙個並使其不能互相攻擊(同一行、同一列、同一斜線上的皇后都會自動攻擊)。
一、 求解n皇后問題是演算法中回溯法應用的乙個經典案例
回溯演算法也叫試探法,它是一種系統地搜尋問題的解的方法。回溯演算法的基本思想是:從一條路往前走,能進則進,不能進則退回來,換一條路再試。
在現實中,有很多問題往往需要我們把其所有可能窮舉出來,然後從中找出滿足某種要求的可能或最優的情況,從而得到整個問題的解。回溯演算法就是解決這種問題的「通用演算法」,有「萬能演算法」之稱。n皇后問題在n增大時就是這樣乙個解空間很大的問題,所以比較適合用這種方法求解。這也是n皇后問題的傳統解法,很經典。
下面是演算法的高階偽碼描述,這裡用乙個n*n的矩陣來儲存棋盤:
1) 演算法開始, 清空棋盤,當前行設為第一行,當前列設為第一列
2) 在當前行,當前列的位置上判斷是否滿足條件(即保證經過這一點的行,列與斜線上都沒有兩個皇后),若不滿足,跳到第4步
3) 在當前位置上滿足條件的情形:
在當前位置放乙個皇后,若當前行是最後一行,記錄乙個解;
若當前行不是最後一行,當前行設為下一行, 當前列設為當前行的第乙個待測位置;
若當前行是最後一行,當前列不是最後一列,當前列設為下一列;
若當前行是最後一行,當前列是最後一列,回溯,即清空當前行及以下各行的棋盤,然後,當前行設為上一行,當前列設為當前行的下乙個待測位置;
以上返回到第2步
4) 在當前位置上不滿足條件的情形:
若當前列不是最後一列,當前列設為下一列,返回到第2步;
若當前列是最後一列了,回溯,即,若當前行已經是第一行了,演算法退出,否則,清空當前行及以下各行的棋盤,然後,當前行設為上一行,當前列設為當前行的下乙個待測位置,返回到第2步;
演算法的基本原理是上面這個樣子,但不同的是用的資料結構不同,檢查某個位置是否滿足條件的方法也不同。為了提高效率,有各種優化策略,如多執行緒,多分配記憶體表示棋盤等。
在具體解決該問題時,可以將其拆分為幾個小問題。首先就是在棋盤上如何判斷兩個皇后是否能夠相互攻擊,在最初接觸這個問題時,首先想到的方法就是把棋盤儲存為乙個二維陣列,然後在需要在第i行第j列放置皇后時,根據問題的描述,首先判斷是在第i行是否有皇后,由於每行只有乙個皇后,這個判斷也可以省略,然後判斷第j列是否有皇后,這個也很簡單,最後需要判斷在同一斜線上是否有皇后,按照該方法需要判斷兩次,正對角線方向和負對角線方向,總體來說也不難。但是寫完之後,總感覺很笨,因為在n皇后問題中這個函式的使用次數太多了,而這樣做效率較差,個人感覺很不爽。上網檢視了別人的實現之後大吃一驚,大牛們都是使用乙個一維陣列來儲存棋盤,在某個位置上是否有皇后可以相互攻擊的判斷也很簡單。
具體細節如下:
把棋盤儲存為乙個n維陣列a[n],陣列中第i個元素的值代表第i行的皇后位置,這樣便可以把問題的空間規模壓縮為一維o(n),在判斷是否衝突時也很簡單,首先每行只有乙個皇后,且在陣列中只佔據乙個元素的位置,行衝突就不存在了,其次是列衝突,判斷一下是否有a[i]與當前要放置皇后的列j相等即可。至於斜線衝突,通過觀察可以發現所有在斜線上衝突的皇后的位置都有規律即它們所在的行列互減的絕對值相等,即| row – i | = | col – a[i] | 。這樣某個位置是否可以放置皇后的問題已經解決。
但是一般來說遞迴的效率比較差,下面重點討論一下該問題的非遞迴實現。
非遞迴方法的乙個重要問題時何時回溯及如何回溯的問題。程式首先對n行中的每一行進行探測,尋找該行中可以放置皇后的位置,具體方法是對該行的每一列進行探測,看是否可以放置皇后,如果可以,則在該列放置乙個皇后,然後繼續探測下一行的皇后位置。如果已經探測完所有的列都沒有找到可以放置皇后的列,此時就應該回溯,把上一行皇后的位置往後移一列,如果上一行皇后移動後也找不到位置,則繼續回溯直至某一行找到皇后的位置或回溯到第一行,如果第一行皇后也無法找到可以放置皇后的位置,則說明已經找到所有的解程式終止。如果該行已經是最後一行,則探測完該行後,如果找到放置皇后的位置,則說明找到乙個結果,列印出來。但是此時並不能再此處結束程式,因為我們要找的是所有n皇后問題所有的解,此時應該清除該行的皇后,從當前放置皇后列數的下一列繼續探測。
分析原文】
/*
33 n皇后問題
n皇后問題是將n個皇后放置在n*n的棋盤上,皇后彼此之間不能相互攻擊。
給定乙個整數n,返回所有不同的n皇后問題的解決方案。
每個解決方案包含乙個明確的n皇后放置布局,其中「q」和「.」分別表示乙個女王和乙個空位置。
*/#include #include #include #include #include using namespace std;
class solution
/*遞迴實現*/
void recursivefun(vector> &solutions, vector&queue, int n, int row)
//if
else//if
}//else
}//else
} /*方法
二、非遞迴實現*/
vector> solvenqueens(int n) //if
else//else
}//while
/*第i行沒有找到可以放置皇后的位置,說明該方案不可行*/
if (-1 == queue[i])
//if
/*回溯到上一行*/
else
//else
}//if
/*找到乙個可行解*/
if (i == n - 1)
//if
++i;
}//while
return solutions;
} /*判斷在r行c列放置皇后,是否合法*/
bool isvalid(vectorqueue, int r, int c)
//for
return true;
} /*將符合要求的結果用字串陣列表示*/
vectorgetresult(vectorqueue)
int len = queue.size();
vectorret(len, string(len,'.'));
for (int i = 0; i < len; ++i)
//for
return ret;
}};int main()
//for
}//for
system("pause");
return 0;
}
github原始碼
/*
34 n皇后問題 ii
根據n皇后問題,現在返回n皇后不同的解決方案的數量而不是具體的放置布局。
樣例比如n=4,存在2種解決方案
*/#include #include #include #include #include using namespace std;
class solution
/*遞迴實現*/
void recursivefun(int &solutions, vector&queue, int n, int row)
//if
else//if
}//else
}//else
} /*方法
二、非遞迴實現*/
int totalnqueens(int n) //if
else//else
}//while
/*第i行沒有找到可以放置皇后的位置,說明該方案不可行*/
if (-1 == queue[i])
//if
/*回溯到上一行*/
else
//else
}//if
/*找到乙個可行解*/
if (i == n - 1)
//if
++i;
}//while
return solutions;
} /*判斷在r行c列放置皇后,是否合法*/
bool isvalid(vectorqueue, int r, int c)
//for
return true;
}};int main()
github原始碼ii N皇后問題(遞迴與非遞迴解法)
最近演算法老師講到了 皇后問題,我順便在這邊總結一下他的思路,主要還是深搜加剪枝 解題思路 用陣列 儲存每個皇后在下標行中的位置 即列 然後進行深搜加剪枝判斷,如果不符合條件了,則回朔 思路很簡單,下面的 包含兩種解法 include include define m 8 修改此處改變皇后的基數 b...
關於回溯法的遞迴與非遞迴 N皇后問題
藍橋杯 基礎練習 2n皇后問題給定乙個n n的棋盤,棋盤中有一些位置不能放皇后。現在要向棋盤中放入n個黑皇后和n個白皇后,使任意的兩個黑皇后都不在同一行 同一列或同一條對角線上,任意的兩個白皇后都不在同一行 同一列或同一條對角線上。問總共有多少種放法?n小於等於8。輸入格式 輸入的第一行為乙個整數n...
遞迴與N皇后問題
遞迴的基本概念 乙個函式呼叫其自身,就是遞迴 遞迴的作用 1 替代多重迴圈 2 解決本來就是用遞迴形式定義的問題 3 將問題分解為規模更小的子問題進行求解 一行只能有乙個皇后,這個根據遊戲規則中的皇后的勢力就可以得知。首先先讓a皇后放在左上角 0,0 b皇后再從第二行找到合適的位置,以此類推c皇后在...