首先看看八皇后问题:
在8X8的国际象棋棋盘上放置8个皇后,使这8个皇后中任意两个不在同一行、同一列、同一斜线上。试求出可能的解法以及总的解法个数。
分析:
(1)此问题的解必然是一个长度为8的向量,即在64个格子中选取8个,即C 8(上)64(下) = 4.426*10^9种方案,但此思路消耗太大
(2)如果我们这样想,设定一个数列loca[8],其中loca[i]代表第i行的皇后所在的列数,由于皇后不在同一行同一列,因此可供选择集合石{0,1,2,3,4,5,6,7},而此时的解集合即是8!= 40320 比(1)的消耗少了很多倍。
此时,我们再来看如何用算法求解这个问题:
按照在上一篇文章(回溯法基本知识)中所讲述的,这个问题可以找到一个解空间,而这个解空间是一个层数为9的完全二叉树,每一个结点都有8个子结点,所以,现在我们已经确定了解空间的结构,下一步是找到他的剪枝函数。
由于从backtrace(0)开始,向下纵深对应于棋盘中的行,所以下一行的元素不会和上一个元素在同一行,现在只要判断这个元素是否和前面已经摆好的皇后在同一列或者同一斜线上即可。
同一列的判断:loca[i] = loca[j]
同一斜线,因为是45°角,或者135°角,在斜率为1时,对点(a,b)和(c,d),a+b=c+d,而对斜率为-1时,a-b = c – d,两者移项可得,a-c = d – b 和 a – c = b – d,因此最终可以归纳为|a-c|=|b-d|。满足剪枝条件的可以直接去掉,剩下的即为正确答案。
将此问题推广,上述的方法实际上是适用于n皇后问题的,所以在代码中只需要改变n的值即可得到不同问题的正确解答。
程序代码如下:(C语言实现)
此代码可动态求解n皇后问题,只需改变#define n 8中的8即可
#include <stdio.h>
#include <stdlib.h>
#define n 8//这里可以任意改变n的值得到N皇后问题的解
int sum = 0;//记录总解的个数
int loca[n] = {0};
int place(int k)
{
int i;
for(i = 0 ; i < k ; i ++)//对前面的t个已放置好的皇后依次考察
if((fabs(i-k)==fabs(loca[i]-loca[k])||loca[i]==loca[k]))
//如果满足在同一斜线或者同一列
return 0;
return 1;
}
void queen(int t)
{
int i;
if(t >= n)//到达最底层无法纵深,sum++
{
sum++;
for(i = 0 ; i < n ; i++)
printf("%d ",loca[i]);
printf("\n");
}
else
{
for(i = 0 ; i < n ; i ++)//对该结点的8个子结点依次搜索
{
loca[t] = i ;//第t行第i列
if(place(t))
queen(t+1);//如果此列可放置,则沿着这点继续搜寻下一层
}
}
}
int main()
{
queen(0);//从第0层开始回溯
printf("%d\n",sum);//输出解的个数
return 0;
}
程序依次输出符合n皇后要求的答案,然后输出总的答案个数。