描述

在8x8的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上。

问:

  1. 有多少种解法?
  2. 这些解法分别是什么?

思路

枚举所有皇后的位置,直到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
 */
posted on 2016-02-03 13:24  yanhh  阅读(152)  评论(0编辑  收藏  举报