八皇后问题,即在一个棋盘上,每行都可以放置一个皇后,但每个皇后都不能影响其他皇后的安全,即所有皇后的位置不能在同一直线上
解决问题方法及思想 : 递归
对每一行而言,从第一个位置进行判断,如果安全,则放置元素,如果不安全,则进行下一个位置的判断,如果本行所有位置都不安全,则返回上一行,重新寻找皇后位置,直到所有情况都寻找完毕
在使用递归之前首先要准备好两个函数实现:
1.判断此时此刻位置是否安全 (只需要判断元素上方,左上方,右上方是否安全,且只要有一个位置不安全,则结束判断!)
2.如果已经完成一个能将皇后安全放置的棋盘,则输出
完整代码:
#include <stdio.h>
#define N 8
#define boolean int
#define TRUE 1
#define FALSE 0
boolean isSafe(int (*chessboard)[N], int row, int col);
void drawChessboard(int (*chessboard)[N]);
void nQueen(int (*chessboard)[N], int row);
void nQueen(int (*chessboard)[N], int row) {
int col;
if(row == N) { //如果行数到达最后一行,说明已经成功找到一个安全放置皇后的棋盘,此时输出该棋盘
drawChessboard(chessboard);
} else {
for(col = 0; col < N; col++) {
if(isSafe(chessboard, row, col)) { //如果这个位置安全
chessboard[row][col] = 1; //将该位置的状态变为"有皇后"的状态,即赋值为1
nQueen(chessboard, row + 1); //寻找下一行皇后的位置
}
chessboard[row][col] = 0; //无论本行是否安全,都应该去掉本位置的皇后,因为下一列需要放置一个皇后
}
}
}
void drawChessboard(int (*chessboard)[N]) {
int i;
int j;
static int count = 0; //如果不加static,则每次调用这个函数,count都会重新被赋值为0!
printf("%d:\n", ++count);
for(i = 0; i < N; i++) {
for(j = 0; j < N; j++) {
printf("%2d", chessboard[i][j]);
}
printf("\n");
}
printf("\n");
}
boolean isSafe(int (*chessboard)[N], int row, int col) { //判断该点位置是否安全
int i;
int j;
//判断元素上方是否有皇后
for(i = row - 1, j = col; i >= 0; i--) {
if(1 == chessboard[i][j]) {
return FALSE;
}
}
//判断元素左上方是否有皇后
for(i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
if(1 == chessboard[i][j]) {
return FALSE;
}
}
//判断元素右上方是否有皇后
for(i = row - 1, j = col + 1; i >= 0 && j < N; i--, j++) {
if(1 == chessboard[i][j]) {
return FALSE;
}
}
return TRUE;
}
int main() {
int chessboard[N][N] = {0}; //创建一个棋盘,并将所有元素赋初值为0,如果放置皇后,则赋值为1
nQueen(chessboard, 0); //将棋盘地址传进,从下标为0的第一行开始进行位置判断
}
一些问题补充说明:
指针与二维数组:
int a[3][4]; a是二维数组的名字,是一个指针常量,它指向的是它所属元素的首元素,它的每一个元素是一个行数组,因此它的指针移动单位是“行”,故a+i指向的是是第i个行数组,即指向a[i]. 该过程也可以描述为: int *p; p = a[0];
对上述a数组,行数组指针定义如下:
int(*p)[4]; 表示数组*p有4个int型元素,分别为(*p)[0], (*p)[1], (*p)[2], (*p)[3], 即p指向的是有4个int型元素的一维数组,即p为行指针
此时,关于nQueen的传参问题就变得简单:
在主函数调用中 : nQueen(chessboard, 0); chessBoard是一个二维数组名称,即一个指针常量,指向第一个行数组
而在函数实现中 : void nQueen(int (*chessBoard)[N], int row); 定义一个行指针chessBoard, 指向N个int型元素(注意形参和实参中的chessBoard是不一样的)
这样实参传的是行地址,而形参指向行元素,没有问题