上个例子(汉诺塔问题),我们讲解了利用递归其中一个作用,将一个问题分解成规模更小的子问题进行求解。现在用N皇后问题开说明一下递归的另一个作用:用递归代替多重循环。
1、问题描述
n皇后问题:输入整数 n,要求 n 个国际象棋的皇后,摆在 n*n 的棋盘上,互相不能攻击,输出全部方案。
输出结果里的每一行都代表种摆法。行里的第 i 个数字如果是 n,就代表第 i 行的皇后应该放在第 n 列。
皇后的行、列编号都是从1开始算。
样例输入:
4
样例输出 :
2 4 1 3
3 1 4 2
2、思路分析
有一个问题是八皇后问题,用八重循环来解决,每一行有一个皇后,用八重循环来遍历所有可能的摆放位置。 现在问题变成 N 皇后问题了,不可能用 n 重循环来解决,现在我们来用递归代替多重循环。
#include<iostream>
using namespace std;
int N;
int QueuePos[100]; //用来存放每一行皇后的位置,最多100行
void NQueue(int k);
int main()
{
cin >> N;
NQueue(0); //从第0行开始摆皇后的位置
return 0;
}
//0~k-1行的皇后都摆好了,现在摆放第k行及其以后的皇后
void NQueue(int k)
{
if (k == N) //N个皇后都摆放好了
{
for (int i = 0; i < N; i++)
cout << QueuePos[i] + 1 << " ";
cout << endl;
return;
}
for (int i = 0; i < N; i++) //第k行的每个位置逐个尝试
{
int j;
for (j = 0; j < k; j++) //第k行第i列是否与前面0~k-1行的皇后冲突
{
if (QueuePos[j] == i || abs(QueuePos[j] - i) == abs(j - k))
break; //冲突,尝试下一个位置
}
if (j == k) //第k行皇后与所有行的皇后都不冲突
{
QueuePos[k] = i; //保存第k个皇后位置i
NQueue(k+1);
}
}
}
首先来说明一个为什么执行 NQueue(0); 就会输出所有的可能结果。原因就是在摆放第 k 行皇后的时候,我们尝试了所有的可能情况,假设 i=0,就是第 k 行的皇后摆放在第0列成立,函数继续递归,打印的这种摆放方法。函数是会返回的,i 参数加1,这时i=1,尝试摆放在第1列是否可以,如何成立,继续递归;如果与前面的皇后的摆放位置冲突了,不继续递归,i++,尝试第k行的下一个位置。所以尝试了所有的方案,会打印出所有的方案。
下面以N=4为例来说明函数的执行过程:
- 执行 NQueue(0);
- 放置第0个皇后,放在第0列,不冲突
- 执行 NQueue(1);
- 放置第1个皇后,放在第0列,冲突;放在第1列,冲突;放在第2列,不冲突
- 执行 NQueue(2);
- 放置第2个皇后,放在第0列,冲突;放在第1列,冲突;放在第2列,冲突;放在第3列,冲突;NQueue(2)返回
- 执行 NQueue(1); …
最后发现第0个皇后放置在第0个位置找不到方案,返回到NQueue(0);放置第0个元素,放置在第1个位置…,如此尝试,直到找到所有的方案。
3、总结
理解递归代替多重循环的执行过程