八皇后问题,是一个古老而著名的问题,是回溯算法的典型例题。该问题是十九世纪著名的数学家高斯1850年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。计算机发明后,有多种方法可以解决此问题。(百度百科)
百度百科提到了两种解法:残卷法和深搜,这两种方式相信大家都可以想到。“残卷法”是确定每一步之后将剩余棋盘不可用的位置都标记出来,代码实现比较繁琐;而提到的深搜的方法需要保存额外的信息,也比较麻烦。
这里,我们注意到:8X8棋盘,8皇后,而8正好是一个字节的比特数!利用C语言的位操作可以轻松的实现对此问题条件限制的判断。OK,我们继续分析这个问题,任意两个皇后不能处于同一行、同一列或同一斜线上,那么有条件限制:
a) 每一行只有一个皇后,这样我们每一步可以确定一个皇后的位置,共8步
b) 每一列只有一个皇后,我们可以将已确定的皇后列位置标记出来(按位或),新增的皇后不能处于这些位置
c) 两皇后不能位于同一斜线如何判断?思考如果两皇后在同一斜线,那么固定一个皇后的列位置,将另一个皇后的列位置左移(或右移)两个皇后的行间距,则两皇后位置重合。
有了如上三种条件限制,同样采用深搜的思路,代码就非常容易了。
[cpp]
view plain
copy
- #include <stdio.h>
- #define QUEEN_MAX 8
- unsigned char chess_board[QUEEN_MAX];
- static int solutions = 0;
- void Queen8(int step)
- {
- int i, j;
- unsigned char position;
- if(step == QUEEN_MAX-1) /* 最后一步 */
- {
- position = 0;
- for(i=0; i<QUEEN_MAX-1; i++)
- position |= chess_board[i];
- position = ~position; /* 确定最后一个皇后的列位置 */
- for(i=0; i<QUEEN_MAX-1; i++)
- { /* 最后一个皇后不能和其它在同一斜线 */
- if((position == chess_board[i]>>(step-i)) ||
- (position == chess_board[i]<<(step-i)))
- {
- return;
- }
- }
- solutions++; /* 解法+1 */
- }
- else
- {
- for(j=0; j<QUEEN_MAX; j++)
- { /* 每行都有8个位置 */
- position = 0x1<<j;
- for(i=0; i<step; i++)
- { /* 当前皇后不能和其它在同一列和同一斜线 */
- if(((position & chess_board[i]) != 0) ||
- (position == chess_board[i]>>(step-i)) ||
- (position == chess_board[i]<<(step-i)))
- break;
- }
- if(i == step)
- { /* 进行下一步 */
- chess_board[i] = position;
- Queen8(step+1);
- }
- }
- }
- return;
- }
- int main()
- {
- Queen8(0);
- printf(“%d\n”, solutions);
- return 0;
- }