N皇后问题, 是一个很经典的递归算法.(recursion), 题目来自国际象棋玩法。
一、问题描述:
在n×n的棋盘上放置n个皇后,任何2个皇后不能放在同一行或同一列或同一斜线上。
输入:
输出:
二、解题思路:
首先用 i (1<= i <= n)表示行,代表 N 个皇后处在不同行上。
再用 x(i)表示列, 代表第 i 个皇后放在 x(i)列上 。
这样来看约束条件:
因为i 已经代表了N个皇后处在不同行上,所以这点可以不用考虑。
如果x(k) = x(i), 那就表示皇后k和皇后i处在同一列, 要各个皇后处在不同列上,就需要 x[k] != x[i].
如果皇后k 和皇后i 处在同一斜线上,那必须有 i + x(i) = k + x(k), 或i - x(i) = k - x(k),
也就是同一斜线上的(X,Y)坐标, 要么X + Y相等(左高右低), 要么X-Y相等(左低右高)。
合并两个公式,也就是 |i -k| = |x(i) - x(k)| , 绝对值用函数abs()即可。
如下图:
这样再利用回溯法,就可以找出解了:
1. 递归法:
#include<stdio.h>
#define N 15
int n; //queuen number
int sum = 0; // solution number
int x[N]; // queen column
int place(int k) //try to put new queen k on x[k].
{
int i;
for(i=1;i<k;i++)
if(abs(k-i)==abs(x[k]-x[i]) || x[k] == x[i])
return 0;
return 1;
}
int queen(int t)
{
int j;
if(t>n && n>0) //when t > n, solution + 1
sum++;
else
for(j=1;j<=n;j++) {
x[t] = j; //queen t on j column
if(place(t)) //if return true, place another queen.
queen(t+1);
}
return sum;
}
int main()
{
int t;
scanf("%d",&n);
t = queen(1);
if(n == 0) //if n = 0, solution = 0.
t = 0;
printf("%d\n",t);
return 0;
}
2. 迭代法, 不使用递归方式,
#include<stdio.h>
#define N 15
int n;
int sum = 0;
int x[N];
int place(int k)
{
int i;
for(i=1;i<k;i++)
if(abs(k-i)==abs(x[k]-x[i]) || x[k] == x[i])
return 0;
return 1;
}
int queen()
{
x[1] = 0;
int t=1;
while(t>0)
{
x[t]+=1;
while(x[t]<=n && !place(t))
x[t]++;
if(x[t]<=n) {
if(t == n)
sum++;
else
x[++t] = 0;
} else
t--;
}
return sum;
}
int main()
{
int t;
scanf("%d",&n);
t = queen();
printf("%d",t);
return 0;
}
在这里我们可以看到,递归回溯非常简单,结构很清晰,但它有一个潜在的问题存在,即当随着变量n的增大,递归法的复杂度也将成几何级增长,也有可能会出现重复的情况,所以我们在解决问题时,如果能用迭代法解决,最好还是不要用递归法,除非你已经对这个递归了如指掌了。
比如说这个N皇后问题,好像当n>60的时候,回溯法就不能完全地解决问题了,这时可以考虑用概率算法来解决,它可以解决很大的基数,只不过结果不是很精确而已。
所以我们在面对一个问题时,具体是使用什么算法还是要结合实际情况来考虑的,目的都是更方便、更准确地解决问题。