問題描述:在8X8的棋盤上放置八個皇后,任意兩個皇后都不能處於同一行、列或者同一斜線上。
分析:每行只能放一個,每列只能放一個。
回溯
如何判段在當前位置是否可以防止皇后
行和列的判斷很簡單,主要是對角線的判斷
在 / 對角線上的點:行 + 列相等
在 \ 對角線上的點:行 - 列相等
通過建立皇后對象的結構體,重載==實現兩個皇后相等的判斷
bool operator==(const Queen& obj){
return (x == obj.x || y == obj.y ||
(x + y) == (obj.x + obj.y) ||
(x - y) == (obj.x - obj.y));
return x == obj.x;
}
建立Queen表示皇后
包含:座標,皇后相等的判斷
struct Queen
{
int x, y;
Queen(int xx = 0, int yy = 0) :x(xx), y(yy){}
bool operator==(const Queen& obj){
return (x == obj.x || y == obj.y ||
(x + y) == (obj.x + obj.y) ||
(x - y) == (obj.x - obj.y));
return x == obj.x;
}
bool operator!=(const Queen&obj){
return *this == obj;
}
void operator=(const Queen&obj){
x = obj.x;
y = obj.y;
}
};
思路
顯而易見,每一行只能放一個皇后,因此我們選擇一行一行的防止皇后。我們將可以的滿足條件的Queen放入vector中。第一個皇后從(0,0)位置開始放置,並將其信息記錄到vector中,然後進入下一行,從(1,0)開始通過在vector中find當前Queen(重載過==)來判斷是否是合法位置,可以放置則入棧,不能則進入(1,1),循環進行。。。。
find函數
bool find(vector<Queen>v, Queen obj){
int i = 0;
for (i = v.size()-1; i >=0; i--){
if (v[i] == obj)
return true;;
}
return false;
}
直到這一行都沒有位置可以放置的時候,說明上次放置的位置不合理,出vector中剔除最後一個元素(等價於出棧),回到剔除元素的位置,換下一個位置判斷。。。
Queen pop(vector<Queen>&v){
Queen q;
q = v[v.size()-1];
v.pop_back();
return q;
}
當成功放置八個皇后後,同樣出棧回到上次的位置,換下一個位置判斷,這樣就可以得到所有的可能。
void placeQueens(int N){
vector<Queen>v;
Queen q(0, 0);
do
{
if (N <= v.size() || N <= q.y){//非法點 出棧
//若size==N說明已經找出一組解了 把當前棧頂同樣出棧 找下一組解
q = pop(v);
q.y++;
}
else
{
while ((q.y < N) && (find(v, q)))
退出while的可能:
//1.q.y>=N跳出邊界,(跳出邊界前沒有找到不相同的 )
//出現這中情況則說明在y<N的範圍內沒有合法點 所以上一次的點是不合法的 會出棧
//2.q.y<N在邊界中合法,則只有後面的條件爲false 即和前面的點都不同
// 這種點就是當前的合法點,入棧。
{
q.y++;
}
if (N>q.y){
v.push_back(q);//記錄當前合法點入棧信息x y
q.x++;//行向下移動找下一行的合法點
q.y = 0;
}
}
if (v.size() >= N){
print(v, N);
//只有合法點纔會入棧 一共有N個合法點 所以當
//size==N時說明棧中的點就是一組解 total++
total++;
cout << total << endl;
}
}
while (0<q.x || q.y<N);
//更容易理解的循環條件是while (!(q.x == 0 && q.y == N));
}
遞歸
相對而言遞歸就簡單的多,遞歸本質上相當於操作系統幫我們完成了入棧出棧的操作,因此我們只需要判斷遞歸的開始條件和終止條件即可。
思路:
我們用一個int ret[9]的數組來記錄皇后的位置,用數組下標表示皇后所在行,下標中對應元素表示列。
用函數void placeQueens2(int queennum)來實現目標,參數爲已經放置的皇后個數,每成功找到一個皇后則在 ret中記錄他的位置,然後
void placeQueens2(queennum+1)找下一個皇后
當下一個皇后找不到時 返回,並從ret中將ret[–queennum]的皇后信息清除(因爲不可用),return遞歸終止條件1
當queennum = 7時說明8個皇后都找到了,遞歸終止條件2
int ret[8] = { -1 };
int q_total = 0;
bool avaiable(int num, int pos){
for (int i = 0; i < num; ++i){
if (ret[i] == pos || (i + ret[i] == num + pos) ||
(i - ret[i] == num - pos))
return false;
}
return true;
}
void print(int ret[]){
for (int i = 0; i < 8;i++){
cout << ret[i] << " ";
}
cout << endl;
}
void placeQueens2(int queennum){
for (int i = 0; i < 8; i++){
if (avaiable(queennum, i)){
ret[queennum] = i;
if (queennum<7)
placeQueens2(queennum + 1);
else{
q_total++;
print(ret);
break;//這裏用break return 甚至什麼都不用結果都一樣
//但是過程是不同的用break效率最高
}
}
}
ret[--queennum] = -1;//用--queennum也可以,因爲只要對皇后個數--後,
//其內容就已經失效了
return;
}