在8x8的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
枚举8个皇后的位置:将皇后甲放到第1行的第1列,在棋盘上标记出皇后甲不能攻击到的位置,接着将皇后乙尝试着放到皇后甲不能攻击到的每一个位置,并在每一次尝试中,标记出皇后乙不能攻击到在每一个位置,然后放皇后丙……直到8个皇后都放完或没有皇后可放时输出结果。
我们先输出每一个解法的ASCII棋盘,接着输出一共有多少种解法。
棋盘的每一个格子有三种情况:
我们用char来表示每个格子。因为以下两个原因:
enum {
QUEEN = 'X', // 放了皇后
AVAILABLE = '_', // 没放皇后但可以放
UNSAFE = '.' // 没放皇后且不能放
};
typedef char Chessboard[8][8];
我们先定义一个辅助函数:
void backtrack(unsigned nextQueenNo, Chessboard board);
它做以下事情:
main()可以这样定义:
int main(int argc, char **argv) {
Chessboard board;
memset(board, AVAILABLE, sizeof(Chessboard));
backtrack(1, board)
printf("Total: %d\n", total);
exit(0);
}
它做以下事情:
注意:ASCII棋盘的输出不在main()中进行,而是在backtrack()中进行。
backtrack函数做以下事情:
total = 0
backtrack(nextQueenNo, board)
if (board已经填好8个皇后了)
输出
++total
else
total = 0
for i in board的所有没放但可放皇后的格子
newBoard = board
在newBoard的i位置放置皇后
给newBoard中,由于在i位置放皇后而导致变成UNSAFE的格子加标签
我们先实现一些复杂的部分,再实现整个函数:
输出
我们给board稍微美化下,加上|标记列,加上\n标记行。
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");
与皇后同一对角线
因为前面两种都很好实现,所以我们只实现第三种。
我们先引入一个真命题:
若点\((x,y)\)与点\((x',y')\)处于同一对角线,则:\[|x'-x|=|y'-y|\]
有了这个命题就好办了。
for (i = 1; i < 8; ++i) {
CHECK_AND_MARK(r + i, c + i);
CHECK_AND_MARK(r + i, c - i);
CHECK_AND_MARK(r - i, c + i);
CHECK_AND_MARK(r - i, c - i);
}
其中,CHECK_AND_MARK宏表示先检查\((r,c)\)是否在board内,若是则在此处标上UNSAFE。
完整实现:
void backtrack(unsigned nextQueenNo, Chessboard board) {
if (nextQueenNo == 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 = nextQueenNo - 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);
}
for (rr = 0; rr < 8; ++rr) { // 列
MARK(rr, c);
}
for (i = 1; i < 8; ++i) { // 对角线
CHECK_AND_MARK(r + i, c + i);
CHECK_AND_MARK(r + i, c - i);
CHECK_AND_MARK(r - i, c + i);
CHECK_AND_MARK(r - i, c - i);
}
backtrack(nextQueenNo + 1, newBoard);
}
}
}
}
其中MARK,CHECK_AND_MARK两个宏的实现:
#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)
在线查看:http://codepad.org/ujLqa4Mj
或者点这里下载文件:http://files.cnblogs.com/files/jt2001/8queens.7z
文件: G:\8queens.7z
大小: 1680 字节
修改时间: 2016年2月 4日, 12:07:10
MD5: 6747715D1B3F92ECA95B71496BF6A5F2
SHA1: A455D3BDB92F1D2F0E9B2A3B3B5AD4A739D411AC
CRC32: 01AB441C