皇后放置的规则:不能同行,不能同列,也不能同对角线(正反双对角线)
先设置一下全局变量
#define N 8
int cnt=0;
char board[N][N];
N 为皇后个数
cnt 为一共有多少种放置方法
board 为棋盘,用于放置皇后和打印结果
主函数如下:
int main() {
memset(board, '0', sizeof(board));
dfs(0);
printf("%d", cnt);
return 0;
}
用memset将board初始化
dfs为算法函数,传入的参数 0 为当前已经放置了的皇后个数,并且在该函数内将要放置第 0+1 个皇后
dfs:
void dfs(int n) { //表示已经放置了n个queen,第n+1个queen要放于何处
//当放置的queen数量等于N
if (n == N) {
cnt++;
display();
printf("\n");
return;
}
int i = n;
for (int j = 0; j < N; j++) {
//检查第i行第j列能否放入
if (check(i, j)) {
//能放入的话
board[i][j] = '1';
dfs(n+1);
//放完之后回溯
board[i][j] = '0';
}
}
}
void dfs(int n){} n 为当前已经放置的皇后个数,本次将要放置第 n+1 个皇后
当已经放置的皇后个数 n==N 时,证明找到了一种解,此时解的总数cnt++,同时使用display把当前解的棋盘布局打印出来方便观察。上述行为做完后return回溯
int i=n 这里的 i 作为行号,因为每一行只能放置一个皇后(皇后不能同行),所以每当放置了一个皇后之后就跳过该行从下一行开始放置,由此也得出了第 n 个皇后将会被放置在第 n 行。因为每放置一个皇后我们都要检查行列和对角线,而通常我们习惯用 i 表示行用 j 表示列,所以这里用 i 作为行号
有了行号之后还要设置列号,每当从新的一行放置皇后时都要从第 0 列开始遍历检查能否放置
check(i,j) 检查第 i 行 第 j 列能否放置
如果能够放入皇后,则把board中当前坐标的内容改为1
dsf(n+1) 放入一个皇后之后开始放入下一个皇后
board[i][j] = '0' 所有皇后都放完之后回溯,把改坐标的内容重新改为0,查找下一种解
接下来的关键就是如何check:
bool check(int i, int j) {
//遍历board
int x = 0; int y = 0;
for (x = 0; x < i; x++) {
for (y = 0; y < N; y++) {
//当 任意一行的j列 或者 board[i][j]的正反对角线 已经有queen
if ((y == j||x+y==i+j||x-y==i-j) && board[x][y] == '1') {
return false;
}
}
}
return true;
}
之前传入的坐标就是 i、j,这里用相同的临时变量接收
新建 x、y用于遍历board,检查是否能在 board[i][j] 放置皇后
判断同行同列是否已有皇后:
因为每次放完一个皇后之后,下一个皇后只能在下一行放,两个皇后在放之前就不可能放在同一行,所以这里只用判断同列。
对 i 之前的每一行每一列进行遍历,当出现 board[x][y] == '1' && y == j 时,证明与 board[i][j] 同列已有皇后,return false;
判断同对角线(从左上往右下)是否已有皇后:
对角线:
我们现在想要在 [ i , j ] 放入皇后,可以发现与 [ i , j ] 同对角线的元素 [ x , y ] 的横纵坐标之差与 [ i , j ] 是一样的,这样当x-y==i-j && [ x , y ] 处已有皇后,return false
反对角线:
与 [ i , j ] 同对角线的元素 [ x , y ] 的横纵坐标之和与 [ i , j ] 是一样的,这样当x+y==i+j && [ x , y ] 处已有皇后,return false