N皇后问题——回溯求解一般方法(C语言)
一、问题描述
八皇后问题十分经典,也就是每两个棋子之间不能处于同一行、同一列、同一对角线。
此类问题,可以视作在约束条件下进行先序(根)遍历,并在遍历过程中剪去那些不满足条件的分支。
如图,就是一个四皇后问题的棋盘状态树,可以看到,在递归过程中不仅有落子行为,还有剪枝行为。
二、问题思路
可以采用回溯求解的一般思路,以下是书本上的伪代码思路。
void Trial(int i, int n) {
// 当进入本函数时,在nxn的棋盘的前i-1行已有满足条件的i-1个棋子
// 现从第i行开始为后续棋子选择位置
// 当i>n时,即为合法布局,输出之
if(i>=n) 出现符合的棋局,输出结果
else {
for(j = 1; j<=n; j++) {
在第i行j列放棋子
if(当前布局合规) Trial(i+1, n);
移除第i行j列的棋子
}
}
}
三、具体实现
其中 i 是行,从 0 开始计数;n 代表每行最大元素数目;board是棋盘数组,由于二维数组的存储可以视为一维数组,因此可以用 board[n*i +j] 表示第 (i, j) 号元素。
//到第 i 行都放了合规的棋子
void Queen(int i, int n, int* board) {
if(i>=n) { //出现符合的棋局,输出结果
sum += 1; //统计有多少解法
printf("result:%d\n", sum);
for(int x = 0; x<n*n; x++) {
printf("%d ", board[x]);
if((x + 1) % n == 0) puts("");
}
} else {
for(int j = 0; j<n; j++) {
board[i*n + j] = 1; //放皇后
if(NoUp(i, j, n, board) && NoDia(i, j, n, board)) //当符合规范,递归放皇后
Queen(i+1, n, board);
board[i*n + j] = 0; //移除皇后
}
}
}
在检测当前棋局是否合规时,由于是从上往下放棋子,因此只需要判断 (i, j) 上方是否有棋子即可。
完整代码如下
#include<stdio.h>
#define MaxSize 12
//计数
int sum = 0;
//检测(i, j)上方元素是否有冲突
bool NoUp(int i, int j, int n, int* board) {
for(int x = 0; x<i; x++) {
if(board[x*n + j] == 1) return false;
}
return true;
}
//检测(i, j)对角线上元素是否有冲突
bool NoDia(int i, int j, int n, int* board) {
int flag = 0;
while(i-flag > 0) { //当(i, j)不在第一行时
flag += 1;
if(j-flag >= 0) { //检测位置是否超边界,处于左上方对角线的元素是否冲突
if(board[i*n + j - (n+1) * flag] == 1) return false;
}
if(j+flag < n) { //检测位置是否超边界,处于右上方对角线的元素是否冲突
if(board[i*n + j - (n-1) * flag] == 1) return false;
}
}
return true;
}
//到第 i 行都放了合规的棋子
void Queen(int i, int n, int* board) {
if(i>=n) { //出现符合的棋局,输出结果
sum += 1; //统计有多少解法
printf("result:%d\n", sum);
for(int x = 0; x<n*n; x++) {
printf("%d ", board[x]);
if((x + 1) % n == 0) puts("");
}
} else {
for(int j = 0; j<n; j++) {
board[i*n + j] = 1; //放皇后
if(NoUp(i, j, n, board) && NoDia(i, j, n, board)) //当符合规范,递归放皇后
Queen(i+1, n, board);
board[i*n + j] = 0; //移除皇后
}
}
}
int main() {
int board[MaxSize*MaxSize] = {0}; //棋盘初始化
Queen(0, MaxSize, board);
return 0;
}
四、后记
通过修改MaxSize的大小,可以拓展到N皇后问题。
在测试过程中,12皇后解的数目达到了5位数,而10皇后的解的数目只有3位数。