八皇后問題三種實現
八皇后問題,是一個古老而著名的問題,是回溯算法的典型案例。該問題是國際西洋棋棋手馬克斯·貝瑟爾於1848年提出:在8×8格的國際象棋上擺放八個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,問有多少種擺法。 高斯認爲有76種方案。1854年在柏林的象棋雜誌上不同的作者發表了40種不同的解,後來有人用圖論的方法解出92種結果。計算機發明後,有多種計算機語言可以解決此問題。(摘自百度)
擺放棋子的規則是:
行從上向下,列從左到右試探。
C++回溯解法
#include <deque>
using namespace std;
static int const N = 8;
//
// 下棋位置
//
struct ChessPoint
{
ChessPoint(){}
ChessPoint(int x,int y)
{
this->x = x;
this->y = y;
}
ChessPoint & operator = (ChessPoint & cp)
{
this->x = cp.x;
this->y = cp.y;
return *this;
}
//橫座標,列
int x;
//縱座標,行
int y;
};
static bool CanPlace(int x,int y,deque<ChessPoint> &vec)
{
deque<ChessPoint>::iterator it = vec.begin();
for(;it!=vec.end();it++)
{
if( it->x == x ||
it->y == y ||
it->x-x == it->y-y ||
x-it->x == it->y-y )
{
return false;
}
}
return true;
}
//
// passPath - 經過的路徑
// lineNum - 現在檢查的行
//
bool GetFreePoint(deque<ChessPoint> &result,ChessPoint &retCP)
{
ChessPoint cp = *result.rbegin();
int x = 0;
int y = 0;
x = cp.x+1;
y = cp.y;
result.pop_back();
//
// 這一行繼續查
//
for(;;)
{
if(x>=N)
{
if(y<0)
{
break;
}
cp = *result.rbegin();
//
//試探下一列;行不變
//
x = cp.x+1;
y = cp.y;
if(x>=N)
{
//
// 此行已經試探完畢,回到上一行
//
result.pop_back();
continue;
}
result.pop_back();
}
if(CanPlace(x,y,result))
{
retCP.x=x;
retCP.y=y;
return true;
}
else
{
x+=1;
}
}
return false;
}
void queen()
{
//
// x -> (0 , N-1) , x 是列
// y -> (0 , N-1) ,y 是行
//
int x = 0;
int y = 0;
//
// 結果存放在一個vector中,有 N 個元素,每行一個
//
deque<ChessPoint> resultVec;
while(1)
{
//
// 出口
//
if(resultVec.size()==N)
{
break;
}
//
// 判斷(x,y)此點是否可以
//
if(CanPlace(x,y,resultVec))
{
//
// 存入結果
//
ChessPoint pp(x,y);
resultVec.push_back(pp);
//
// 行加一
//
y+=1;
//
// 列歸零
//
x=0;
}
else
{
if(x < N)
{
//
// (x,y) 此點不能匹配,移動到下一列
//
x+=1;
//
// 沒有列可以選擇了
//
if(x>=N)
{
//
// 沒有找到匹配的列,則回溯到上一行,找到一個沒有經過的座標
//
ChessPoint po;
if(GetFreePoint(resultVec,po))
{
x = po.x;
y = po.y;
}
else
{
break;
}
}
}
else
{
//
// 沒有找到匹配的列,則回溯到上一行,找到一個沒有經過的座標
//
ChessPoint po;
if(GetFreePoint(resultVec,po))
{
x = po.x;
y = po.y;
}
else
{
break;
}
}
}
}
}
C回溯解法
<span style="font-size:12px;">//
// 使用回溯法解皇后問題
// 當下一行找不到合適的位置,則返回到上一行,試探下一個位置,知道找到合適的位置;
// 當找到一個解以後,放棄這個位置,回溯找下一個合適的位置,直到找不到合適的位子,
// 就將所有解找到了。
//
//
// 要放8個皇后
//
static const int N = 8;
//
// 已經放置的皇后個數
//
static int count = 0;
//
// 解的個數
//
static int solution = 0;
//
// 下棋位置
//
struct ChessPoint
{
ChessPoint(){}
ChessPoint(int x,int y)
{
this->x = x;
this->y = y;
}
ChessPoint & operator = (ChessPoint & cp)
{
this->x = cp.x;
this->y = cp.y;
return *this;
}
//橫座標,列
int x;
//縱座標,行
int y;
};
//
// canPlace:是否可以放置皇后呢?
// line - 行,col - 列
//
static bool CanPlace(int x,int y,unsigned char* vec)
{
for(int i=0;i<count;i++)
{
if( i == y ||
vec[i] == x ||
i-y == vec[i]-x ||
y-i == vec[i]-x )
{
return false;
}
}
return true;
}
//
// passPath - 經過的路徑
// lineNum - 現在檢查的行
//
static bool GetFreePoint1(unsigned char* result,ChessPoint &retCP)
{
int x = 0;
int y = 0;
x = result[count-1]+1;
y = count-1;
count-=1;
//
// 這一行繼續查
//
for(;;)
{
if(x>=N)
{
if(y<0)
{
break;
}
x = result[count-1]+1;
y = count-1;
if(x>=N)
{
count-=1;
continue;
}
count-=1;
}
if(CanPlace(x,y,result))
{
retCP.x=x;
retCP.y=y;
return true;
}
else
{
x+=1;
}
}
return false;
}
//
// printResult:打印結果
// resultVec - 結果數組
//
void printResult(unsigned char *resultVec)
{
printf("-------%d皇后的解 第%d組---------\n",N,++solution);
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
{
if(resultVec[i]==j)
{
printf("1 ");
}
else
{
printf("0 ");
}
}
printf("\n");
}
}
//優化實現
void queen1()
{
//
// x -> (0 , N-1) , x 是列
// y -> (0 , N-1) ,y 是行
//
int x = 0;
int y = 0;
//
// 結果存放在數組中,數組的索引代表行,值代表對應的列
//
unsigned char resultVec[N];
while(1)
{
//
// 出口
//
if(count == N)
{
printResult(resultVec);
//
// 繼續找下一個解
//
ChessPoint po;
if(GetFreePoint1(resultVec,po))
{
//
// 個數減一,並且回溯
//
x = po.x;
y = po.y;
}
else
{
//
//再沒有解了
//
break;
}
}
//
// 判斷(x,y)此點是否可以
//
if(CanPlace(x,y,resultVec))
{
//
// 存入結果
//
resultVec[y]=x;
count+=1;
//
// 行加一
//
y+=1;
//
// 列歸零,從第一列試
//
x=0;
}
else
{
if(x < N)
{
//
// (x,y) 此點不能匹配,移動到下一列
//
x+=1;
//
// 沒有列可以選擇了
//
if(x>=N)
{
//
// 沒有找到匹配的列,則回溯到上一行,找到一個沒有經過的座標
//
ChessPoint po;
if(GetFreePoint1(resultVec,po))
{
x = po.x;
y = po.y;
}
else
{
//
// 找不到合適的座標了
//
break;
}
}
}
else
{
//
// 沒有找到匹配的列,則回溯到上一行,找到一個沒有經過的座標
//
ChessPoint po;
if(GetFreePoint1(resultVec,po))
{
x = po.x;
y = po.y;
}
else
{
//
// 找不到合適的座標了
//
break;
}
}
}
}
}
</span>
C 遞歸解法
<span style="font-size:10px;">//
// 這是教課書上的解法,遞歸解決。
// queen3 以每行作爲一個單位
//
//
// 解的個數,最後8皇后得到92組解
//
static int solution = 0;
//
// 放幾個皇后?
//
static const int N=8;
//
// 已經放置的個數,如果爲8個,則找到一組解
//
static int count = 0;
//
// 這裏存放一組解
//
static int result[N];
//
// canPlace:是否可以放置皇后呢?
// line - 行,col - 列
//
static bool canPlace(int line,int col)
{
for(int i=0;i<count;++i)
{
if( i == line ||
col == result[i] ||
i-line==col-result[i]||
line-i == col-result[i])
{
return false;
}
}
return true;
}
void printResult()
{
for(int i=0;i<N;++i)
{
for(int j=0;j<N;++j)
{
if(j == result[i])
{
printf("1 ");
}
else
{
printf("0 ");
}
}
printf("\n");
}
printf("\n");
}
//
// queen3
// line - 從哪行開始?
//
bool queen3(int line)
{
bool bFound = false;
//
// 當填滿的時候打印
//
if(count == N)
{
printResult();
++solution;
printf(" %d \n",solution);
//count -= 1;
}
//
// 當到了最後一行的時候,運行到這裏說明已經找到了一條可行的路徑;
// 而繼續查找就要回退一行,然後繼續下一列,因此返回false,回退一行
//
if(line==N)
{
return false;
}
//
//i 代表列
//
for(int i=0;i<N;i++)
{
if(canPlace(line,i))
{
result[line]=i;
count+=1;
if(line<N)
{
if(!queen3(line+1))
{
count-=1;
}
else
{
bFound = true;
}
}
}
}
//
// 如果沒有合適的位置,則返回false,然後就會回退一行,繼續試探下一個位置
//
if(!bFound)
{
return false;
}
return true;
}</span>
//主函數調用:
void queen();
void queen1();
bool queen3(int line);
int _tmain(int argc, _TCHAR* argv[])
{
queen1();
queen();
queen3(0);
}
每一種解法是一個文件,主函數是一個文件。