一、八皇后問題
八皇后問題,是一個古老而著名的問題,是回溯算法的典型案例。該問題是國際西洋棋棋手馬克斯·貝瑟爾於1848年提出:在8×8格的國際象棋上擺放八個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,問有多少種擺法。 高斯認爲有76種方案。1854年在柏林的象棋雜誌上不同的作者發表了40種不同的解,後來有人用圖論的方法解出92種結果。
二、算法思路:
首先我們分析一下問題的解,我們每取出一個皇后,放入一行,共有八種不同的放法,然後再放第二個皇后,同樣如果不考慮規則,還是有八种放法。
於是我們可以用一個八叉樹來描述這個過程。從根節點開始,樹每增加一層,便是多放一個皇后,直到第8層(根節點爲0層),最後得到一個完全八叉樹。
我們先對問題解的結構做一個約定:
用gEightQueen[i]來表示,在第i行,皇后放在了(i,gEightQueen[i])這個位置。
我們考慮條件:
一共有四個,不能同一行,不能同一列,不在同一左右對角線上。
同一行、同一列好解決,
左對角線是x座標和y座標的和不相等,右對角線是x-y不相等,有正負之分。
三、代碼,主要用到了遞歸和回溯,註釋非常清楚
#include<iostream>
using namespace std;
int num=0;
static int gEightQueen[8] = { 0 }, gCount = 0;//用gEightQueen[i]來表示,在第i行,皇后放在了gEightQueen[i]這個位置。
void print()//輸出每一種情況下棋盤中皇后的擺放情況
{
cout<<"No."<<num<<endl;
for (int i = 0; i < 8; i++)
{
int inner;
for (inner = 0; inner < gEightQueen[i]; inner++)
{
cout << "0 ";
}
cout <<"# ";
for (inner = gEightQueen[i] + 1; inner < 8; inner++)
{
cout << "0 ";
}
cout << endl;
}
cout << "==========================\n";
}
int check_pos_valid(int loop, int value)//檢查是否存在有多個皇后在同一行/列/對角線的情況,loop和value爲要檢查的位置
{//index和data是皇后的行和列 loop是檢查位置的行,value是檢查位置的列
int index;
int data;
for (index = 0; index < loop; index++)//檢查index以上所有行的皇后,是否跟當前位置衝突
{
data = gEightQueen[index];//初始設爲0,行列範圍是1-8
if (value == data)
return 0;
if ((index + data) == (loop + value))
return 0;
if ((index - data) == (loop - value))
return 0;
}
return 1;
}
void eight_queen(int index)//index是當前檢查行
{
int value;
for (value = 0; value < 8; value++)//檢查index行上所有列
{
if (check_pos_valid(index, value))//如果index行上有不會導致衝突的列座標,就放一個皇后
{
gEightQueen[index] = value;
if (7 == index)//如果放到第8行,就計數、打印,函數結束 。
{//這裏就是回溯,在每一個節點處向下把所有的可能做完了才向上返回
gCount++, num++,print();
//gEightQueen[index] = 0; 這兩句是遞歸結束後,把數據清零
return;
}
eight_queen(index + 1);//沒到第八行,就檢查下一行
//gEightQueen[index] = 0; 遞歸下層返回時,把數據清零,這裏刪掉沒關係是因爲每次使用都是賦值覆蓋後,沒有預設它本來是什麼值
}
}
}
int main(int argc, char*argv[])
{
eight_queen(0);
cout << "total=" << gCount << endl;
return 0;
}
四、運行結果