【演算法複習二】八皇后問題 ---- 回溯
一,問題描述
在8X8格的國際象棋上擺放八個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,問有多少種擺法。
二,分析
採用逐步試探的方式,先從一個方向往前走,能進則進,不能進則退並嘗試另外的路徑。首先我們來分析一下國際象棋的規則,這些規則能夠限制我們的前進,也就是我們前進途中的障礙物。一個皇后q(x,y)能被滿足以下條件的皇后q(row,col)吃掉
1)x=row(縱向不能有兩個皇后)
2) y=col(橫向不能有兩個皇后)
3)col + row = y+x;(斜向正方向)
4)col - row = y-x;(斜向反方向)
遇到上述問題之一的時候,說明我們已經遇到了障礙,不能繼續向前了。我們需要退回來,嘗試其他路徑。
我們將棋盤看作是一個8*8的陣列,這樣可以使用一種蠻幹的思路去解決這個問題,這樣我們就是在8*8=64個格子中取出8個的組合,C(64,8) = 4426165368,顯然這個數非常大,在蠻幹的基礎上我們可以增加回溯,從第0列開始,我們逐列進行,從第0行到第7行找到一個不受任何已經現有皇后攻擊的位置。
前面四列的擺放如上圖則 第五列,我們會發現找不到皇后的安全位置
第五列的時候,擺放任何行都會受到上圖所示已經存在的皇后的攻擊,這時候我們認為我們撞了南牆了,是回頭的時候了,我們後退一列,將原來擺放在第四列的皇后
(3,4)拿走,從(3,4)這個位置開始,我們在第四列中尋找下一個安全位置為(7,4),再繼續到第五列,發現第五列仍然沒有安全位置,回溯到第四列,此時第四列也是一個死衚衕了,我們再回溯到第三列,這樣前進幾步,回退一步,最終直到在第8列上找到一個安全位置(成功)或者第一列已經是死衚衕,但是第8列仍然沒有找到安全位置為止
總結一下,用回溯的方法解決8皇后問題的步驟為:
1>從第一列開始,為皇后找到安全位置,然後跳到下一列
2>如果在第n列出現死衚衕,如果該列為第一列,棋局失敗,否則後退到上一列,在進行回溯
3>如果在第8列上找到了安全位置,則棋局成功。
三,原始碼(精選自網友解答)
回溯法非遞迴
#include<iostream>
using namespace std;
#define N 8
//N代表皇后數
void queen()
{
int Count=0; //計算總共的解的數量
int column[N+1]; //column[m]=n 表示第m列,第n行放置了皇后,這裡下表並從0開始
int row[N+1]; //row[m]=1表示第m行沒有皇后,=0表示有皇后
int b[2*N+1]; //b[m]=1表示第m條主對角線沒有皇后,
int c[2*N+1]; //c[m]=1表示第m條次對角線沒有皇后,=0表示有皇后
int numQueen=1; //計數已經放置的皇后數目,當numQueen=N時候則表示已經完成探測
int good=1; //good=1表示沒有發生衝突,good=0表示發生衝突
//初始化這些標記
for(int j=0;j<N+1;++j)
{
row[j]=1; //沒有皇后
}
for(int j=0;j<2*N+1;++j)
{
b[j]=c[j]=1;
}
column[1]=1;
column[0]=0; //初始化第一行第一列,第二行第二列放置皇后
do
{
//沒有發生衝突,則繼續向下探測,增加皇后或者判斷當前是否是解
if(good)
{
//當前皇后數是解,列印,繼續向下探測
if(numQueen==N)
{
Count++;
cout<<"找到解"<<endl;
for(int j=1;j<N+1;++j)
{
cout<<j<<"列"<<column[j]<<"行"<<endl;
}
//最後一個棋子向下移動,移動到本列最後一個
while(column[numQueen]==N)
{
numQueen--; //皇后數減1,即列數減1,回溯
//回溯後將該列以及該列最後一行狀態位修改
//第numQueen列column[numQueen]行處狀態位置修改
row[column[numQueen]]=1;
b[numQueen+column[numQueen]]=1;
c[N+numQueen-column[numQueen]]=1;
}
column[numQueen]++; //回溯至上一行,向上一行的下一列繼續探測
}
//當前不是解,那麼繼續向下探測
else
{
//改變該位置對應標誌
row[column[numQueen]]=0;
b[numQueen+column[numQueen]]=0;
c[N+numQueen-column[numQueen]]=0;
//本次位置沒有發生衝突,也不是正確解,那麼就應該向下探測下一列的第一行
column[++numQueen]=1;
}
}
//如果當前發生了衝突,就在本列繼續向下,如果到了本列最後一行,則回溯到上一列
else
{
while(column[numQueen]==N) //到了本列最後一行,還是衝突,那麼回溯到上一列
{
numQueen--;
row[column[numQueen]]=1;
b[numQueen+column[numQueen]]=1;
c[N+numQueen-column[numQueen]]=1;
}
column[numQueen]++; //發生衝突了,又沒有到本列的最後一行,那麼在本列繼續向下一行探測
}
//檢測放置了這個位置後是否衝突
good=row[column[numQueen]]&b[numQueen+column[numQueen]]&c[N+numQueen-column[numQueen]];
}while(numQueen);
cout<<N<<"皇后總共找到解:"<<Count<<"個"<<endl;
}
int main()
{
queen();
system("pause");
return 0;
}