首先,缅怀数学王子——高斯,在没有计算机且图论相对不完善的时代,手动得出八皇后的76种解法。
学习了递归有两年多,没用递归做解决过八皇后的问题。今天,经过一番内心的挣扎,终于写出了这个问题的解法。
接下来进入正题:
问题:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列且同一斜线上,问有多少种摆法。
分析问题:个人理解,看到问题,第一反应是画图,但是8*8的图有点大。于是简化问题,做2*2的图。
当将的第一个皇后(黑色代表皇后)放入棋盘,我就知道,此山不能容二虎。于是我们从3*3开始。
嗯,不出意外,3*3的图最多放两个。
4*4这里就不放图了,还是不行,只能放3个满足条件的皇后。
But当棋盘变为5*5时,就可以实现5个皇后不会攻击到彼此。
例图:(只是其中一种情况)
现在,我们回头再看问题。这个问题,有点像数独。嗯,数独中有一个规则是:同一个数字,一行一列只能出现一次。而八皇后问题也有条件:每个皇后所处的行列不能有其他的皇后。而这个八皇后多了一个条件:这个皇后所处的斜线上也不能幽皇后。这里提供一个思路:将数独中的数字可以完美转换成一个n*n矩阵的,而矩阵在进行列变化时,其行列的总和不变,也就是说,这个数独的本质没变,他还是数独,不会出现同一行或者同一类有相同的数字。将这一思想引入八皇后问题,我们是否可以先初始化一个满足一个不处于同一行、同一列的八皇后?这个条件很容易满足,如下图:
当所有皇后出现在对角时,就初始了皇后的位置,且满足所有不处于同一行、同一列。接下来我们只用进行列边换,以完成多个皇后出现在同一斜线上的问题。
接下来,当我们进行行类变换时就会发现,当行边换与列边换变换可以达到同样的效果:
当我们将初始情况进行x1<--->x2变化时与y1<--->y2效果一样。那么,我们就可以只进行一种边换来解决这个问题。
这样做,直接将二维问题转化为一维。
现在,我们将每个皇后打标记,标记的号码为他的初始行数(这里行数与列数相等)。则会出现(以为例五皇):12345。现在我们要解决的问题是,如何边换12345的顺序,使他再转化到棋盘中时,同一斜线没有多个皇后。
我们现分析,出现同一斜线的必要条件:回到二维棋盘,当且仅当两个棋子的形成的直线斜率为1或者-1时,在同一斜线时。也就是:x2-x1=y1-y2或者y2-y1。好了,分析到这,问题基本思路就解决了。
还有一个要说的是,这一串数字的(x,y)属性体现在哪里?
答:数字所处数字串的位置为y值,该处所对应的值为x值。(x,y是等价的,是可以互换的,但边换的过程中不能互换)
如何用递归解决八皇后问题
个人思路,无优化,仅供参考。
我们可以用遍历树解决来解决这一问题:
求解的过程类似于树的遍历,有多分枝的出口(递归中递的过程),每次在向下递的同时判断,这个操作是否可行。
而返回的路径只有一条就是上一层(递归中递的过程),及就是一个树节点可以有多个孩子,他的父节点只有一个。
这是递归的思想,再向其中加入判断,就可以完成不在同一斜线的可能。
代码如下:
package 实验;
public class eight_queens {
static int X=0;//记录解法总数
static int num;//定义皇后总数
public static void main(String[] args) {
num=5;
int []Q=new int[num];//初始化棋盘(一维棋盘)
E_Q(Q,num);
System.out.println(X);//输出解法的总数
}
private static void E_Q(int arr[],int n) {//递归体
if(n==0) {//出口
for(int i=0;i<num;i++)
System.out.print(arr[i]);//输出正确的序列(一维的棋盘)
System.out.println();
X++;
}
else if(n==num) {//第一位皇后进入时不需要判断
for(int i=1;i<=num;i++) {
arr[0]=i;
E_Q(arr,n-1);
}
}
else {
/*
*可以下行的条件:
* 1、该数字 该串没有isNoin
* 2、不在一条斜线上
*/
for(int i=1;i<=num;i++) {
if(isNoin(arr,n,i)&&isYx(arr,n,i)) {
arr[num-n]=i;
E_Q(arr,n-1);
}
}
}
}
private static boolean isYx(int arr[],int n,int i) {//该数字是否与之前的重复
int x1,y1,x2=num-n,y2=i;
for(int j=0;j<=num-n-1;j++) {
x1=j;y1=arr[j];
if(x1-x2==y1-y2||x1-x2==y2-y1) return false;
}
return true;
}
private static boolean isNoin(int arr[],int n,int i) {//该数字是否与之前的在一行
for(int j=0;j<num-n;j++)
if(arr[j]==i)
return false;
return true;
}
}