八皇后问题之递归算法实现
什么是八皇后问题?
在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上(在国际象棋中,皇后可以可横直斜走,且格数不限,吃子与走法相同),问有多少种摆法?
该怎么解决八皇后问题?
我们要解决这个问题,首先要先想到棋盘要怎么表示,因为我们要棋盘上摆放皇后,看有几种摆法?
这里我们用二维数组来表示棋盘,只要申请一个8*8大小的数组,就可以表示棋盘了。
#define width 8
boolean ChessBoard[width][width]; //width = 8
这里的boolean是类型定义(其实就是unsigned char ChessBoard[8][8]),而这个类型定义是在mec.h中:
#ifndef _MEC_H_
#define _MEC_H_
typedef unsigned char boolean; //类型定义
#define TRUE 1
#define FALSE 0
#define NOT_FOUND -1
#define SETB(value, index) (value |= 1 << ((index) ^ 7))
#define CLRB(value, index) (value &= ~(1 << ((index) ^ 7)))
#define GETB(value, index) (((value) & (1 << ((index) ^ 7))) != 0)
int skipBlank(const char *str);
boolean isRealStart(int ch);
#endif
棋盘表示结束了,我们如何表示皇后呢?
很简单,我们在二维数组中用 “1” 表示皇后,用 “0” 表示没有皇后。
刚开始时,棋盘中没有皇后,全都是“ 0 ”:
boolean ChessBoard[width][width] = {0}; //width = 8
棋盘和皇后的表示解决后,下面就是该如何判断皇后放在安全的位置,让任意两个皇后都不能处于同一行、同一列或同一斜线上。我们上面已经用“ 1 ”表示有皇后了,所以这里我们只需要判断我们即将放皇后的位置的左上方、上方、右上方有没有皇后。若有,则此位置不能放皇后;若无,则此位置可以放皇后。
代码如下:
boolean isSafe(boolean (*ChessBoard)[width], int row, int col)
{
int i;
int j;
for (i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--)//判断左上方
{
if (ChessBoard[i][j] == 1)
{
return FALSE;
}
}
for (i = row - 1, j = col; i >= 0; i--) //判断上方
{
if (ChessBoard[i][j] == 1)
{
return FALSE;
}
}
for (i = row - 1, j = col + 1; i >= 0 && j < width; i--, j++)//判断右上方
{
if (ChessBoard[i][j] == 1)
{
return FALSE;
}
}
return TRUE;
}
在完成这个判断后,下面就要使用递归来完成对皇后的摆放。
void chess(boolean(*ChessBoard)[width], int row)
{
int col;
if (row >= width) //当row = 8时,八皇后已经摆放成功了。这就是一种解法。
{
showChess(ChessBoard);//我们把它输出。
return;
}
for (col = 0; col < width; col++) //从一行一列到一行八列判断是否安全。
{
if (isSafe(ChessBoard, row, col)) //如果安全,这个位置赋值1,表示已经放皇后了
{
ChessBoard[row][col] = TRUE;
chess(ChessBoard, row + 1); //开始递归,第一次递归时,(row + 1) = 2,所以在递归函数中,row = 2, 然后从二行一列到二行八列判断是否安全.....
ChessBoard[row][col] = FALSE; //当调用的递归函数结束后,都要返回这里把这个位置赋值为零。因为这个递归调用如果结束了,证明有两种可能:
//1、八皇后已经摆放成功,这需要尝试下一种解法,这个位置需要赋值为零。
//2、八皇后摆放不成功(即在某一行,任何一个位置通过isSafe()判断都不安全),这需要尝试下一种摆放方法,这个位置需要赋值为零。
}
}
}
如果上述代码和注释看的不太明白的话,建议在纸上把棋盘画出来,按照代码递归的进行一遍。
完整代码
#include<stdio.h>
#include "mec.h" //这里的mec.h的代码在上面,需要创建一个 .h 的头文件,才可以编译。
#define width 8
boolean isSafe(boolean(*ChessBoard)[width], int row, int col);
void showChess(boolean(*ChessBoard)[width]);
void chess(boolean(*ChessBoard)[width], int row);
void chess(boolean(*ChessBoard)[width], int row)
{
int col;
if (row >= width)
{
showChess(ChessBoard);
return;
}
for (col = 0; col < width; col++)
{
if (isSafe(ChessBoard, row, col))
{
ChessBoard[row][col] = TRUE;
chess(ChessBoard, row + 1);
ChessBoard[row][col] = FALSE;
}
}
}
void showChess(boolean(*ChessBoard)[width])
{
int i;
int j;
static int count = 0;
printf("第%d个解 :\n", ++count);
for (i = 0; i < width; i++)
{
for (j = 0; j < width; j++)
{
printf("%5d ", ChessBoard[i][j]);
}
printf("\n");
}
}
boolean isSafe(boolean (*ChessBoard)[width], int row, int col)
{
int i;
int j;
for (i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) //判断左上方
{
if (ChessBoard[i][j] == 1)
{
return FALSE;
}
}
for (i = row - 1, j = col; i >= 0; i--) //判断上方
{
if (ChessBoard[i][j] == 1)
{
return FALSE;
}
}
for (i = row - 1, j = col + 1; i >= 0 && j < width; i--, j++) 判断右上方
{
if (ChessBoard[i][j] == 1)
{
return FALSE;
}
}
return TRUE;
}
int main()
{
boolean ChessBoard[width][width] = {0};
chess(ChessBoard, 0);
return 0;
}
最后,感谢~铁血教主 !
有问题可以评论留言~