问题描述:
输入一正整数n,要求n个国际象棋皇后,摆在n*n的棋盘上,互相不能攻击,即每个皇后的上下左右和对角线上仅有皇后自身。输出全部方案。
输入:
正整数N
输出:
N皇后问题的全部摆法。输出的结果里的每一行代表一种摆法。行里的第i个数字如果是n,就代表第i行的皇后应该放在第n列。皇后的行列号都是从1开始算。
样例输入:
4
样例输出:
2 4 1 3
3 1 4 2
问题的分析及解决:
我们通常碰到的都是8皇后问题,如果说皇后数量固定的话,这个问题可以理解为是一个枚举问题。就拿8举例,我们可以写个8重循环,第一行,皇后从第一个格子开始放,然后第二行皇后从第一个格子一直放到第八个格子,判断是否符合要求,第三行以此类推,直至8个皇后摆放在棋盘上满足要求,输出摆放的情况。
但是这种代码的编写方式存在一个问题,即便是给出固定的皇后的数值,仍然要写好多层的嵌套循环。如果n的值较大,这代码撸起来让人就有点抓狂了。这个时候就要考虑如何将这么多的循环量减少,那么递归一定是将多层嵌套循环简化的一个方式。我们会发现,每一层的嵌套循环所执行的内容是相同的,那么只要一层循环就可以了,剩下的交给递归就可以搞定了。
代码思路为:定义一个函数NQueen(int k)用于递归求解,入参k从0开始,即从棋牌的第一行开始放皇后,每一列都尝试放一个,完成放置后,调用NQueen(1);然后尝试开始放第二行,第二行的皇后要和第一行的皇后位置进行对比;完成第二行的皇后放置后,调用NQueen(2);开始放置第三行皇后,第三行皇后的放置的位置需要与前2行皇后所放置的位置进行对比,完成放置后,调用NQueen(3),以此类推。如果发现皇后没有合适位置放时,且没有循环到第n个皇后,则无法继续向后调用NQueen(k)这个函数,说明递归调用终止,即该皇后的摆法是不可行的。一直递归到NQueen(N)时,输出当前QueenPos的参数值。
有些递归算法题是逆向递归的思路,假设n-1的方法已知,去求解n的情形,例如:爬楼梯算法,汉诺塔算法等。而这题是从初始值0开始,然后累加1进行递归的。这种递归的方式可以理解为正向递归。具体的实现方式如下所示。
代码
#include <iostream>
#include <cmath>
using namespace std;
int N;
int queenPos[100];//记录每一横行中,皇后所放的列位置
void NQueen(int k);
void main()
{
cin >> N;
NQueen(0);//从数组起始位置开始,进行递归求解
}
void NQueen(int k)
{
if (k == N)
{
for (int i = 0; i < N; i++)
cout << queenPos[i] + 1 << " ";
cout << endl;
}
else
{
for (int i = 0; i < N; i++)//当前行遍历所有列的皇后位置是否可行,i+1位列位置
{
int j;
for (j = 0; j < k; j++)//与已排列好的皇后的位置进行对比,k为已排列好的数
{
if (queenPos[j] == i || abs(queenPos[j] - i) == abs(k - j))
break;//如果当前皇后所放置的位置与已知皇后的位置发生冲突,则跳出循环
}
if (j == k)
{
queenPos[k] = i;
NQueen(k + 1);//正向递归求解k+1
}
}
}
}