八皇后问题
问题
八皇后问题是经典回溯算法问题,指的是一张8*8的棋盘,放八颗棋子,使得任意两个棋子之间不在同一行同一列,且不在斜对角线上,求其有多少种解法。
分析
先考虑手动模拟这个算法,从第一行开始放,不能同行,所以每一行放一个,放的时候看跟之前的有没有同列或同对角线,没有就可以放下去,放够八个就是一个满足的解。得出一个可行解并不难,难在把所有可行解都找到,这时候就到计算机出马了。
引用一个数组,x[i]。其中,i代表行数,x[i]的具体数值代表列。任意两个棋子,用x[i]和x[j]表示,不同行:i!=j,不同列:x[i]!=x[j] ,不在同一对角线上:abs((j-k))!=abs(x[j]-x[k]),abs代表取绝对值,满足这三个条件既可放棋。
int place(int k) //判定条件
{
for (int i = 1; i<k; i++) //依次与以下棋子进行比较
{
if (x[i]==x[k]||abs(i-k)==abs(x[i]-x[k])) {
return 0;
}
}
return 1;
}
放棋步骤可以用递归加一个循环完成,递归对于新的一行,循环对于该行的列。对于递归,具体思考起来很复杂,不过这个是电脑的工作。编写起来,只需要完成两点,递归跳出条件和递归语句。条件为下够八个棋子,既i>8时就不能在下棋子了。语句为该行所有列进行判定,满足条件往下递归。
void traverse(int t) //递归遍历
{
if (t>num) //递归跳出条件
{
sum++; //解法加一
}
else {
for (int j=1; j<=num; j++) //对每列进行判定
{
x[t]=j; //对列进行赋值
if (place(t)) //满足条件,往下递归
{
traverse(t+1);
}
}
}
}
在主函数里进行数值的初始化,函数的调用即可完成。最后得出的结果为92种。
完整代码
#include <stdio.h>
#include <math.h>
//类比于八叉树,抽象出x[i],i为行,x[i]的数值为列。判定条件有2,1是同行同列不等,既i!=k,x[i]!=x[k],2是不在对角线上,既abs((j-k))!=abs(x[j]-x[k]),abs为取绝对值。
static int x[9]; //static设置全局变量
static int num;
static int sum;
int place(int k) //判定条件
{
for (int i = 1; i<k; i++) {
if (x[i]==x[k]||abs(i-k)==abs(x[i]-x[k])) {
return 0;
}
}
return 1;
}
void traverse(int t) //递归遍历
{
if (t>num) //递归跳出条件
{
sum++; //解法加一
}
else {
for (int j=1; j<=num; j++) {
x[t]=j;
if (place(t)) //满足条件,往下递归
{
traverse(t+1);
}
}
}
}
void main()
{
num =8; //八行八列
sum =0;
traverse(1); //从第一行开始挨个放
printf("解法共有:""%d""种\n",sum);
}