八皇后问题是经典的数学与计算机科学难题,在8x8国际象棋棋盘上放置八个皇后,使任何两个皇后都不能互相攻击。
### 有关8*8国际象棋八皇后问题
#### 一、问题背景及描述
**八皇后问题**是在8×8的国际象棋棋盘上摆放8个皇后,要求没有任何一个皇后能攻击到其它任何一个皇后,也就是说没有两个或两个以上的皇后占据棋盘上的同一行、同一列或同一对角线。这个问题最早由国际象棋爱好者马克斯·贝塞尔(Max Bezzel)于1848年提出,后来成为计算机科学中的经典问题之一。
#### 二、数据结构与算法描述
##### 2.1 数据结构
在实际应用中,对于这一类问题,我们通常需要找出所有可能的解集,或者在某些约束条件下寻找最优解。为了实现这一目标,我们可以采用**回溯法**和**栈**这两种数据结构和技术。
- **回溯法**:这是一种通过尝试解决子问题并回退来解决问题的方法。如果某一步的选择导致无法达到最终解,则会撤销这一步选择,返回上一步继续尝试其他可能性。
- **栈**:作为一种后进先出(LIFO)的数据结构,在本问题中用于记录每一步的决策状态。
##### 2.2 算法思想
从第1行开始逐行放置皇后,每放置一个皇后都需要依次对第1至第8列进行试探,并尽可能选择较小的列数。如果当前试探的列位置是安全的,则将该行的列位置保存在栈中,然后继续在下一行寻找安全的位置;如果当前试探的列位置不安全,则尝试使用下一列进行试探,当所有8列都未找到安全位置时,则退栈回溯到上一行,修改栈顶保存的皇后位置,继续试探。
#### 三、算法实现
##### 3.1 算法抽象描述
1. 初始化当前行为1,当前列为1。
2. 当前行号小于等于8时循环执行以下操作:
- 从当前列开始逐列试探,寻找安全的列号。
- 如果找到了安全的列号,则放置皇后,并将列号记录在栈中,然后将下一行设置为当前行,第1列设置为当前列。
- 否则,退栈回溯到上一行,移除该行已放置的皇后,并将该皇后所在列的下一列设置为当前列。
##### 3.2 算法求精
为了更精确地实现上述算法,需要确定相应的存储结构和数据类型:
- `i` 和 `j` 分别表示当前行和列。
- 使用数组 `s[1..8]` 表示顺序栈,栈空间的下标值表示皇后所在的行号,栈的内容是皇后所在的列号。
- 定义布尔型变量 `a[9], b[17], c[17]` ,其中:
- `a[j]` 为真时,表示第 `j` 列上无皇后。
- `b[k]` 为真时表示斜向“”方向的第 `k` 条对角线上无皇后。
- `c[k]` 为真时表示斜向“”方向的第 `k` 条对角线上无皇后。
在位置 `(i,j)` 上放置皇后后,更新 `a[j], b[i+j] 和 c[i-j+9] 的值为假,以表示这些位置已被占用。如果当前行号等于8,则输出棋盘状态;否则继续进行下一步试探或回溯操作。
#### 四、代码实现
这段代码实现了八皇后问题的基本算法,并通过回溯法找到了所有可能的解:
```c
void eight_queens() {
int i = 1, j;
while (i <= 8) { // 当前行号小于等于8时循环执行以下操作:
for(j=1; j<=8; j++) { // 从当前列开始逐列试探,寻找安全的列号
if(a[j] && b[i+j] && c[i-j+9]) break;
}
if (j <= 8) {
a[j]=b[i+j]=c[i-j+9]=false; // 更新棋盘状态为已占用
s[i]=j; // 将列号记录在栈中
if(i == 8) {
print(); // 输出当前棋盘状态的函数
movequeen(8, j); // 回溯操作,将皇后移回上一行的位置
i--;
j = s[i];
movequeen(i, j);
j++;
} else {
i++; // 继续进行下一步试探或回溯操作
j=1;
}
} else {
if (i > 0) { // 如果当前行号大于0,