n皇后问题 递归 C语言,n皇后问题 递归和非递归

问题引入 八皇后问题

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

问题中涉及了回溯算法,它就是一种深度优先搜索算法,每走一步,选择其中的一种可能解,满足就继续走下一步,不满足就回到上一步选择另一种可能的解

下图有助于理解皇后问题的回溯法解决(有其它的解法)

331e9ab65f33485dd06141a32ee6248b.png

在该问题中了解了回溯算法的流程之后我们需要考虑什么时候回溯,也就是每次放置皇后需要考虑的问题:

放置皇后时是否存在同一行,同一列,互为为对角线位置的皇后存在?

假设该皇后放置在坐标(i,j)的位置,每次检查时检查前i-1行就不会涉及到同一行的情况,同一列问题只需要在放置前n-i行的时候标记皇后所在的列数就能解决,而对角线位置则需要前i-1个皇后的坐标与当前皇后的坐标斜率不为正负1

在很多情况下八皇后都被推广到n皇后、2n皇后等,并且有一些附加的条件需要考虑,但是归根到底都是对回溯算法的一种考查。

先看看非递归版的

这里我以我洛谷的一道题目 P1219八皇后 来展示N皇后的非递归解法

81066401fa875edf62f8a62a04ccf57c.png

输入输出样例

输入

6

输出

2 4 6 1 3 5

3 6 2 5 1 4

4 1 5 2 6 3

4

下方是我的题解和注释

#include

#include

#include

using namespace std;

int queen[14];//判断行

int col[14];//判断列,一开始以queen[i]的值表示皇后位于第几列(递归版的就是这么做的)

//但是每次检查的时候都要遍历queen[],最后一个测试点超时了,改了一下,增加了一个数组进行表示

int n;

int total = 0;

bool HasSpace(int i, int j)//检查当前棋盘格子是否可以放置皇后

{

if (col[j])return false;//检查同一列

for (int k = 1; k < i; k++)//检查时只要检查到第i行之前

if (abs(k - i) == abs(queen[k] - j))return false;//对角线为正负1

return true;

}

void Print()//打印皇后的位置

{

for (int i = 1; i <= n; i++)

cout << queen[i] << " ";

cout << endl;

}

int main()

{

int i, j;

int temp = 0;//temp初始化为0

//memset(queen,0,sizeof(queen));因为queen[]和col[]是全局变量自动初始化为0

//memset(col, 0, sizeof(col));如果不是设置为全局的需要手动初始化

cin >> n;

for (i = 1; i <= n; i++)

{

int flag = 1;

for (j = temp + 1; j <= n; j++)

{

temp = 0;//用完temp重置为0

if (HasSpace(i, j)) {

queen[i] = j;

col[j] = 1;//表示该列有皇后

flag = 0;//表示可以放

if (i == n) {//满足n个皇后都放完

total++;

if (total <= 3)Print();//根据题意打印前三种情况

/*完成一种情况,回溯*/

//每次回溯都要把上一行的标记去除,成功的情况下当前行的标记也要去掉

col[queen[i]] = 0;//去掉当前行标记

queen[i] = 0;

i -= 2;//去掉上一行标记

temp = queen[i + 1];//记录回溯到上一个的位置

col[temp] = 0;

queen[i + 1] = 0;

}

break;

}

}

if (flag) {//没找到,回溯

if (i == 1 && j == n + 1) {

break;//回溯到第一行末表示结束

}

i -= 2;//去掉上一行标记,因为第一个for循环后面有i++的条件,所以这里i减少2

temp = queen[i + 1];//记录回溯到上一个的位置,下一次循环j从temp+1开始

col[temp] = 0;//将该列置0

queen[i + 1] = 0;

}

}

cout << total << endl;

return 0;

}

递归版

递归版的算法一般比较代码整洁,效率差,理解起来也不太容易

下方代码采用C语言描述(因为大学课程安排问题,平常都是几种语言混用)

(注意:此代码并非针对上方的P1219八皇后题目的题解)

#include

#include

#define MAX 6

int queen[MAX];//下标代表行,值代表列(注意下标从0开始)

int HasSpace(int n)//n,queen[n],n皇后是否能放置在queen[n]列上

{

int i;

for ( i = 0; i < n ; i++)//扫描前n-1行

{

/*queen[i]==queen[n]表示同一列有皇后 两个abs相等表示有位于同一对角线的皇后*/

if (queen[i] == queen[n] || abs(i - n) == abs(queen[i] - queen[n]))

{

return 0;

}

}

return 1;

}

void PrintfQueen()

{

int i;

for (i = 0; i < MAX; i++)//输出每一行皇后的位置

printf("%d", queen[i]+1);//数组的下标从0开始,输出结果要加1

printf("\n");

}

void PlaceQueen(int n)//表示放置第n个皇后

{

/*

看起来并没有非递归版的直观易理解,

因为它的回溯过程是通过递归时的压栈出栈来实现的

当一个皇后放置成功,进行PlaceQueen(n+1)放置下一个皇后

当该皇后放置失败,会跳出当前的PlaceQueen(n+1)回到PlaceQueen(n)然后j++继续搜索

可以模拟一下递归过程来理解

*/

int j;

for (j = 0; j < MAX; j++)

{

queen[n] = j;//先把皇后放下去,再判断是否放置正确

if (HasSpace(n)) {//如果放置正确,分两种情况

if (n == MAX - 1)PrintfQueen();//放满则打印

else PlaceQueen(n + 1);//否则放置下一个皇后

}

}

}

int main()

{

PlaceQueen(0);//从第一行的皇后开始放

return 0;

}

最后

前文也有提过n皇后问题有其它的解法

例如通过位运算和数学构造法,奈何笔者才疏学浅,感兴趣的可以自己搜索

标签:递归,int,queen,问题,放置,回溯,皇后

来源: https://blog.csdn.net/weixin_44339734/article/details/100979360

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值