问题描述:
在8×8的棋盘上,放置8个皇后(棋子),使两两之间互不攻击。所谓互不攻击是说任何两个皇后都要满足:
(1)不在棋盘的同一行;
(2)不在棋盘的同一列;
(3)不在棋盘的同一对角线上。
求:这8个皇后中的每一个应该摆放在哪一列。
算法分析:
数组Column、Down、Up分别用来标记冲突,Column数组代表列冲突,从Column[1]~Column[8]代表第1列到第8列,如果某列上已经有皇后,则为1,否则为0;
数组Down代表主对角线冲突,为Down[i-j+7](行号-列号+7),主对角线共有15条,即从b[0]~b[14],如果某条主对角线上已经有皇后,则为1,否则为0;
数组Up代表从对角线冲突,为Up[i+j](行号加列号),从对角线也有15条,即从c[0]~c[14],如果某条从对角线上已经有皇后,则为1,否则为0;
其中主对角线中行i和列j相减的规律如下图所示:
从图中可以看出,每条主对角线上的值相同且相互之间不重复,因此可以将其看成一个标志进行判断。同理,观察次对角线中行i和列j中的值,他们相加的规律如下图所示:
利用这一规律判断对角线的情况。
1)需要在棋盘的( i, j ) 位置摆放一个皇后的时候,可以通过Column数组、Down数组和Up数组的相应元素,来判断该位置是否安全;
2)当已经在棋盘的( i, j ) 位置摆放了一个皇后以后,就应该去修改Column数组、Down数组和Up数组的相应元素,把相应的列和对角线设置为不安全。
3)最后根据递归回溯的方法来实现本问题的目标。
实现函数如下:
int Queen[9]={0}; //第i行皇后所在的列;
int Column[9]={0}; //第j列是否安全,{0, 1}
int Down[15]={0}; //记录每一条从上到下的对角线,是否安全,{0,1}
int Up[15]={0}; //记录每一条从下到上的对角角线,是否安全,{0,1}
int num=0; //记录解的个数
void TryQueen(int i) // 摆放第 i 行的皇后
{
if(i<9)
{
for(int j=1;j<9;j++) // 尝试把该皇后放在每一列
{
if(Column[j]||Down[i-j+7]||Up[i+j-2])
continue; // 失败
Queen[i]=j; // 把该皇后放在第j列上
Column[j]=1;
Down[i-j+7]=1;
Up[i+j-2]=1;
if(i==8) // 已找到一种解决方案
{
num++;
for(int k=1;k<9;k++)
cout<<setw(3)<<Queen[k];
cout<<endl;
}
else
TryQueen(i+1); // 摆放第i+1行的皇后
Queen[i]=0; // 回溯,把该皇后从第j列拿起
Column[j]=0;
Down[i-j+7]=0;
Up[i+j-2]=0;
}
}
}