描述
在8x8的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上。
问:
- 有多少种解法?
- 这些解法分别是什么?
思路
枚举所有皇后的位置,直到8个皇后都放完或没有皇后可放时输出结果。
为了方便编程,我们先输出每一个解法,接着输出一共有多少种解法。
代码
/*
* 阅读代码顺序:从下至上
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MARK(r, c) do { \
if (newBoard[r][c] == AVAILABLE) { \
newBoard[r][c] = UNSAFE; \
} \
} while (0)
#define CHECK_AND_MARK(r, c) do { \
if (0 <= r && r < 8 && 0 <= c && c < 8 && \
newBoard[r][c] == AVAILABLE) { \
newBoard[r][c] = UNSAFE; \
} \
} while (0)
/*
* 3: queens函数的辅助宏
*/
/*
* 储存某一时刻的棋盘信息
*/
enum {
QUEEN = 'X', // 放了皇后
AVAILABLE = '_', // 没放皇后但可以放
UNSAFE = '.' // 没放皇后且不能放
};
typedef char Chessboard[8][8];
void queens(Chessboard board, unsigned n, unsigned *total) {
/* 在棋盘board上,从第n个皇后枚举到第8个皇后,将解法总和加到total上
* 如果遇到解法,则输出
*/
if (n == 9) { // 输出
int r, c;
for (r = 0; r < 8; ++r) {
for (c = 0; c < 8; ++c) {
printf("%s%c%s",
(c == 0) ? "|" : "",
board[r][c],
(c == 7) ? "|\n" : "|");
}
}
printf("\n");
++*total;
} else {
int r, c;
for (r = n - 1; r < 8; ++r) {
for (c = 0; c < 8; ++c) {
if (board[r][c] != AVAILABLE) continue;
Chessboard newBoard;
memcpy(newBoard, board, sizeof(Chessboard));
newBoard[r][c] = QUEEN;
int rr, cc, i;
for (cc = 0; cc < 8; ++cc) { // 行
MARK(r, cc); // 标上UNSAFE
}
for (rr = 0; rr < 8; ++rr) { // 列
MARK(rr, c);
}
for (i = 1; i < 8; ++i) { // 对角线
// 若点(x,y)与点(x',y')处于同一对角线,则
// |x'-x| = |y'-y|
CHECK_AND_MARK(r + i, c + i); // 假如坐标合法,标UNSAFE
CHECK_AND_MARK(r + i, c - i);
CHECK_AND_MARK(r - i, c + i);
CHECK_AND_MARK(r - i, c - i);
}
queens(newBoard, n + 1, total);
}
}
}
}
/*
* 2: queens函数及Chessboard类型
*/
int main(int argc, char **argv) {
Chessboard board;
memset(board, AVAILABLE, sizeof(Chessboard));
unsigned total = 0;
queens(board, 1, &total);
printf("Total: %d\n", total);
exit(0);
}
/*
* 1: Main
*/