问题描述
所谓八皇后问题,是指在8×8的国际象棋棋盘上放置8个皇后,保证任意2个皇后都无法相互攻击的问题。国际象棋中的皇后可以向8个方向移动任意格
现已在棋盘上摆放了k个皇后,且这k个格子的位置已给出。请编写一个程序,根据给出的k个有皇后的格子,输出已摆放8个皇后的国际象棋棋盘
输入:
第1行输入整数k。接下来k行输入已放有皇后的格子,每个格子用2个整数r、c表示。r、c分别为从0开始的国际象棋棋盘的行、列编号
输出:
输出表示8×8国际象棋棋盘的字符串,放有皇后的格子用"Q"表示,其他用"."表示
限制:
每个输入有唯一解。
输入示例
2
2 2
5 3
输出示例
......Q.
Q.......
..Q.....
.......Q
...Q....
.Q......
....Q...
讲解
最直接的方法就是穷举出8个皇后的所有摆放方法,然后依次检查其是否满足题中条件。棋盘共有8×8=64格,每次要选择8个格放皇后,因此总共有 C 64 8 = 4426165368 C^8_{64}=4426165368 C648=4426165368种组合。就算考虑到2个皇后无法同时出现在1行,即每行只能有一个皇后,那也有 8 8 = 16777216 8^8=16777216 88=16777216种组合。再加上2个皇后无法同时出现在同一列,也有 8 ! = 40320 8!=40320 8!=40320种组合
相对地,使用下述回溯法求解八皇后问题,要远远比遍历上述所有组合快得多:
在第1行的任意位置摆放皇后
在第2行中,选择不会被第1行皇后攻击的格子摆放第2个皇后
…
前 i 行放好 i 个皇后且保证它们不会相互攻击后,在第(i + 1)行寻找不会被任意一个皇后攻击的格子,摆放第(i + 1)个皇后
如果不存在满足上述条件的格子,则返回第 i 行继续寻找下一个不会被攻击的格子。如果不存在满足该条件的格子,则继续返回(i - 1)行
像这样,系统地尝试所有可能得出正确解的状态,当发现当前状态得不到解时中断搜索并返回(以当时中断的位置为起点)上一状态继续搜索,这样的手法称为回溯。图的深度优先搜索就是基于回溯的算法
在八皇后问题中,为记录格子是否会被其他皇后攻击,我们需要准备下列数组变量。这里的N=8
row[N]:如果row[x]为NOT_FREE,则x行受到攻击
col[N]:如果col[x]为NOT_FREE,则x列受到攻击
dpos[2N-1]:如果dpos[x]为NOT_FREE,则斜向左下的x列受到攻击
dneg[2N-1]:如果dneg[x]为NOT_FREE,则斜向右下的x列受到攻击
只要row[i]、col[i]、dpos[i+j]、dneg[i-j+N-1]中有任意一个是NOT_FREE,格子(i, j)就会受到攻击。也就是说,当row[i]、col[i]、dpos[i+j]、dneg[i-j+N-1]全都为FREE时,皇后可以放置在该格子中
AC代码如下
#include<iostream>
#include<cassert>
using namespace std;
#define N 8
#define FREE -1
#define NOT_FREE 1
int row[N], col[N], dpos[2 * N - 1], dneg[2 * N -1];
bool X[N][N];
void initialize() {
for(int i = 0; i < N; i++) { row[i] = FREE, col[i] = FREE; }
for(int i = 0; i < 2 * N - 1; i++) { dpos[i] = FREE; dneg[i] = FREE; }
}
void printBoard() {
for(int i = 0; i < N; i++) {
for(int j = 0; j < N; j++) {
if(X[i][j]) {
if(row[i] != j) return;
}
}
}
for(int i = 0; i < N; i++) {
for(int j = 0; j < N; j++) {
cout<<( (row[i] == j) ? "Q" : ".");
}
cout<<endl;
}
}
void recursive(int i) {
if(i == N) {//成功放置皇后
printBoard(); return;
}
for(int j = 0; j < N; j++) {
//如果(i, j)受到其他皇后攻击,则忽略该格子
if(NOT_FREE == col[j] ||
NOT_FREE == dpos[i + j] ||
NOT_FREE == dneg[i - j + N -1]) continue;
//在(i, j)放置皇后
row[i] = j; col[j] = dpos[i + j] = dneg[i - j + N -1] = NOT_FREE;
//尝试下一行
recursive(i + 1);
//(i, j)拿掉摆放在(i,j)的皇后
row[i] = col[j] = dpos[i + j] = dneg[i - j + N -1] = FREE;
//皇后放置失败
}
}
int main(){
initialize();
for(int i = 0; i < N; i++)
for(int j = 0; j < N; j++) X[i][j] = false;
int k; cin>>k;
for(int i = 0; i < k; i++) {
int r,c; cin>>r>>c;
X[r][c] = true;
}
recursive(0);
return 0;
}