问题描述
会下国际象棋的人都很清楚:皇后可以在横、竖、斜线上不限步数地吃掉其他棋子。如何将8个皇后放在棋盘上(有8 * 8个方格),使它们谁也不能被吃掉!这就是著名的八皇后问题。 对于某个满足要求的8皇后的摆放方法,定义一个皇后串a与之对应,即a=b1b2…b8,其中bi为相应摆法中第i行皇后所处的列数。已经知道8皇后问题一共有92组解(即92个不同的皇后串)。给出一个数b,要求输出第b个串。串的比较是这样的:皇后串x置于皇后串y之前,当且仅当将x视为整数时比y小。
输入数据
第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数b(1 <= b <= 92)
输出要求
n行,每行输出对应一个输入。输出应是一个正整数,是对应于b的皇后串
输入样例
2
1
92
输出样例
15863724
84136275
分析:从n×n个格子中选择n个格子摆放皇后。可见解空间树为子集树。
使用maze[MAX][MAX]来表示棋盘,maze[i][j]=0 表示(I,j)位置为空,maze[i][j]=1 表示(I,j)位置摆放有一个皇后。
全局变量way表示总共的摆放方法数目。
使用queen(t)来摆放第t个皇后。queen(t) 函数符合子集树时的递归回溯范式。当t>N时,说明所有皇后都已经摆 放完成,这是一个可行的摆放方法,输出结果;否则,遍历棋盘,找皇后t所有可行的摆放位置,placeQueen(i,j) 判断皇后t能否摆放在位置(i,j)处,如果可以摆放则继续递归摆放皇后t+1,如果不能摆放,则判断下一个位置。
placeQueen(row,col)函数首先判断位置(row,col)是否合法,继而判断(row,col)处是否已有皇后,有则冲突,返回0,无则继续判断行、列、斜方向是否冲突。斜方向分为左上角、左下角、右上角、右下角四个方向,每次从(row,col)向四个方向延伸一个格子,判断是否冲突。如果所有方向都没有冲突,则返回1,表示此位置可以摆放一个皇后。
代码:
#include<stdio.h>
#include<string.h>
#include<math.h>
# define MAX 4
int maze[MAX][MAX];//棋盘 0表示空白 1表示有皇后
int placeQueen(int row, int col){
int i,j;
if(maze[row][row] == 1)
return 0;
for(j=0; j<MAX; j++)//同一行
if(maze[row][j] == 1)
return 0;
for(i=0; i<MAX; i++)//同一列
if(maze[i][col] == 1)
return 0;
/* i表示从当前点(row,col)向四个斜方向扩展的长度
左上角 \ / 右上角 i=2
\/ i=1
/\ i=1
左下角 / \ 右下角 i=2
*/
for(i=1; i<MAX; i++)//左上
if((row-i >= 0) && (col-i >= 0))
if(maze[row-i][col-i] == 1)
return 0;
for(i=1; i<MAX; i++)//右上
if((row-i >= 0) && (col+i < MAX))
if(maze[row-i][col+i] == 1)
return 0;
for(i=1; i<MAX; i++)//左下
if((row+i < MAX) && (col-i >= 0))
if(maze[row+i][col-i] == 1)
return 0;
for(i=1; i<MAX; i++)//右下
if((row+i < MAX) && (col+i < MAX))
if(maze[row+i][col+i] == 1)
return 0;
return 1;
}
void queen(int nowQueen){
if(nowQueen > MAX){//放置完成
printf("完成一次配置\n");
for(int i=0; i<MAX; i++){
for(int j=0; j<MAX; j++)
printf("%3d",maze[i][j]);
printf("\n");
}
} else{
for(int i=0; i<MAX; i++)
for(int j=0; j<MAX; j++)
if(placeQueen(i,j)){//能放置
maze[i][j] = 1;
queen(nowQueen + 1);
maze[i][j] = 0;
}
}
}
int main(){
int i,j,count;
memset(maze,0,sizeof(maze));
queen(1);//放置第一个皇后
printf("\n");
}
该问题还有更优的解法。充分利用问题隐藏的约束条件:每个皇后必然在不同的行(列),每个行(列)必然也只有一个皇后。这样我们就可以把N个皇后放到N个行中,使用Pos[i]表示皇后i在i行中的位置(也就是列号)(i = 0 to N-1)。这样代码会大大的简洁,因为节点的子节点数目会减少,判断冲突也更简单。