判断一个位置上能否放皇后,只需在三个方向上进行判断(下、右下、左下)。
关键数据结构定义:
int m_chessboard[N][N]; //定义棋盘
enum{ N = SIZE + 2}; //10 * 10 的棋盘
0表示位置为空,1表示皇后,2表示边界
位置:
struct Pos : public Object
{
Pos(int px=0, int py=0) : x(px),y(py) { }
int x;
int y;
};
方向定义:
下:(0, -1)
左下:(-1, -1)
右下:(1, -1)
//初始化三个方向的方向数据
m_direction[0].x = -1;
m_direction[0].y = -1;
m_direction[1].x = 0;
m_direction[1].y = -1;
m_direction[2].x = 1;
m_direction[2].y = -1;
算法思路:
先定义一个类模板,用来扩展成N皇后问题:
template <int SIZE>
class QueueSolution : public Object
{
protected:
enum{ N = SIZE + 2}; //设置边界
struct Pos : public Object
{
Pos(int px=0, int py=0) : x(px),y(py) { }
int x;
int y;
};
int m_chessboard[N][N]; //定义棋盘
Pos m_direction[3]; //检查三个方向是否有皇后
LinkList<Pos> m_solution; //存储解决方案 链表
int m_count; //解决方法数量
void init()
{
m_count = 0;
for(int i=0; i<N; i += (N-1)) //初始化边界
{
for(int j=0; j<N; j++)
{
m_chessboard[i][j] = 2; //"*"
m_chessboard[j][i] = 2; //"*"
}
}
for( int i=1; i<=SIZE; i++) //初始化棋盘
{
for(int j=1; j<=SIZE; j++)
{
m_chessboard[i][j] = 0;
}
}
//初始化三个方向的方向数据
m_direction[0].x = -1;
m_direction[0].y = -1;
m_direction[1].x = 0;
m_direction[1].y = -1;
m_direction[2].x = 1;
m_direction[2].y = -1;
}
设计一个打印棋盘的函数:
void print()
{
for(m_solution.move(0); !m_solution.end(); m_solution.next()) //遍历解决方案的链表
{
cout<< "(" <<m_solution.current().x << "," <<m_solution.current().y<<")";
}
cout<<endl;
for(int i=0; i<N; i++)
{
for(int j=0; j<N; j++)
{
switch (m_chessboard[i][j])
{
case 0:cout<< " " ; break;
case 1:cout<< "#" ; break;
case 2:cout<< "*" ; break;
}
}
cout<<endl;
}
cout << endl;
}
实现三个方向上的检查:
bool check(int x, int y, int d) //检查三个方向上是否有别的皇后
{
bool flag = true;
do //在当前的位置数据上不停地加上方向数据,来判断
{
x += m_direction[d].x;
y += m_direction[d].y;
flag = flag &&(m_chessboard[x][y] == 0); //检查是否有皇后,
}
while (flag);//如果为真,则继续检查,直到边界
return (m_chessboard[x][y] == 2); //如果为2,则说明直到边界,此方向上都没有别的皇后,x,y已经被加到了边界
}
检查第j行是否可以放置皇后:
void run(int j)
{
if( j <= SIZE)
{
for(int i=1; i<=SIZE; i++)
{
if( check(i, j, 0) && check(i, j, 1) && check(i, j, 2)) //判断三个方向是否已经有了皇后
{
m_chessboard[i][j] = 1;
m_solution.insert(Pos(i, j));
run( j+1 ); //递归调用第J+1行是否可以放皇后
m_chessboard[i][j] = 0; //如果产生回溯,则清空标记
m_solution.remove(m_solution.length() -1);
}
}
}
else //说明前八行都可以放皇后
{
m_count++;
print();
}
}
补充构造函数和公有成员函数:
public:
QueueSolution()
{
init();
}
void run()
{
run(1); //从第一行开始放置皇后
cout<< "Total:" <<m_count<<endl; //打印八皇后问题解决方案的数量
}
};
注意上述程序中的加黑位置。
测试代码:
int main()
{
QueueSolution<8> qs;
qs.run();
return 0;
}
打印结果请读者自行测试,共92种解法。
程序运行后的栈存储区专供函数调用使用;
栈存储区用于保存实参、局部变量、临时变量等;
利用栈存储区能够方便的实现回溯算法;
八皇后问题是栈回溯的经典应用。