N皇后问题
给定一个N*N的棋盘,要在棋盘上摆放N个皇后,
并且满足N个皇后中任意两个皇后都不处于
同一行、同一列、同一斜线上(正斜线、反斜线)
再中级软件设计师考试中,是用一维数组来表示每一个皇后的行列位置(而不是用二维数组)。
第i个皇后(即放在第i行上的皇后)的位置表示为:(i, q[i])。
i 表示行数;q[i] 表示列数。
总行数为N,假设N=4,则:
i 表示行数,
当i=0时,表示还没有开始往棋盘上放皇后;当i=4时,表示尝试到了最后一行。
q[i] 表示列数,
q[i]从1到4逐渐增大,表示在第i行上从1到4逐列移动,依次尝试是否是合法的位置。当q[i]>4时,表示超出了棋盘的范围,也就是该行没有找到合法的位置。
第i行元素:Qi
第j行元素:Qj
核心思想:
// 判断是否在同一列上
Qi列==Qj列
// 判断是否在同一斜线上
|行号之差|=|列号之差|,即:|Qi行-Qj行|=|Qi列-Qj列|
#include<stdio.h>
#include<math.h> //
#define N 4 // 有4个皇后
int answer = 0; // 方案数
int q[N+1];
/*
q[i]表示第i个元素
q[i]中存储的数字,表示的是是第i个皇后的列号(第i行的皇后当前位于哪列)
q[i]=0时,表示棋子还没有放上棋盘
*/
// 检查第j个皇后(放在第j行的皇后)的位置是否合法——(如果不合法则返回0,如果合法则返回1)
// 思路:从第1行到第j-1行逐行检查,检查这些行上已放置的皇后的位置和第j行上的皇后的位置是否有冲突
int check(int j){
int i;
for(i=1; i<j; i++){
if((q[i]==q[j])||(abs(i-j)==abs(q[i]-q[j]))){
return 0;
}
}
return 1;
}
// 通过调研上边这个方法,来找出N个皇后的位置(即求解N皇后方案)
方法1:非递归方法(循环迭代)
题面已经给出的前提是:每个元素按行往下放,所以不需要判断是否是在同一行上
(不存在两个元素放在同一行的情况)
最终输出满足条件的方案——这四行的元素分别位于哪一列上。
// 求解N皇后的方案(找出N个皇后的位置)
void queen(){
int i;
// N个皇后的位置赋初值,都赋值为0(表示都还没有放入棋盘)
for(i=1; i<=N; i++){
q[i]=0;
}
int j=1;
while(j>=1){
// 从第1行开始,每一行从列下标0位置开始往后,依次向后边的列尝试各位置
q[j]=q[j]+1;
// 如果当前位置不合法,就向后移动一列
// 不断向后移动尝试,要么是在4列之中找到了一个合法位置时停下来,要么是在尝试到了第5列时(超出范围)停下来
while(q[j]<=N && !check(j)){
q[j]=q[j]+1;
}
/*
刚刚尝试摆放第j个皇后,在第j行上从前到后逐个尝试最后停下,判断停下来的时候该皇后的位置是那种情况:
(1)在第j行上的前4列之中找到了一个合法位置时停下来(表示第j个皇后找到了一个合法的位置)
(2)在尝试到了第5列时因为超出范围停下来(表示第j个皇后没有找到了一个合法位置)
*/
if(q[j]<=N){
// 表示第j个皇后找到了一个合法的位置
if(j==N){
// 表示当前已经尝试到了最后一行(已经尝试到了最后一个皇后)
answer=answer+1;
printf("方案%d:",answer);
for(i=1; i<=N; i++){
printf("%d ",q[i]);
}
printf("\n");
} else {
// 表示当前找到合法位置的皇后还不是最后一个皇后(还没有尝试到最后一行),继续摆放下一个皇后
j=j+1;
}
} else {
// 表示第j个皇后没有找到一个合法位置
// 回溯
q[j] = 0; // 将第j个皇后移出棋盘(下一次尝试摆放第j行时,从第一列开始重新尝试)
j = j-1; // 回溯(回退到上一行,尝试上一行中的下一种可能性)
// 当j减小到0的时候,表示所有的可能性已经都尝试过一遍了,所以while后边的条件是j>=1
}
}
}
//运行代码
int main(){
queen();
return 0;
}
方法2:递归方法
check()方法以及之前的代码是一样的
区别主要在于queen()方法
// 求解N皇后的方案(找出N个皇后的位置)
// j代表行数
void queen(int j){
int i;
for(i=1; i<=N; i++){
q[j] = i; // 第j行上的皇后,从第1列到第4列的位置依次尝试
if(check(j)){
// 若check(j)为真,则表示第j行上的皇后的当前位置(j, q[j])合法
// 若check(j)为假,则表示第j行上的皇后的当前位置(j, q[j])不合法,不需要做其他操作,直接尝试下一列是否合法就行了(也就是循环中的i++,然后把i赋值给q[j])
if(j==N){
// 当前已经找到了最后一个皇后(最后一行的皇后位置已确定)
answer=answer+1;
print("方案%d:",answer);
for(i=1; i<=N; i++){
printf("%d ",q[i]);
}
printf("\n");
} else {
// 当前找到的皇后还不是最后一个皇后(最后一行的皇后位置还未确定)
// (递归摆放下一个皇后的位置)
queen(j+1);
}
} else {
// 若check(j)为假,则表示第j行上的皇后的当前位置(j, q[j])不合法,不需要做其他操作,直接尝试下一列是否合法就行了(也就是循环中的i++,然后把i赋值给q[j])
}
}
}
//运行代码
int main(){
// 从第1行开始放,如果位置合法,则递归(开始尝试在第2行放置),知道第N=4行为止
queen(1);
return 0;
}
递归方法调用时的实现原理,可以用“栈”来理解。