问题描述
在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。
解题思路:
首先要明白一点,所有的皇后是不在同一行的,所以我们能保证每行最多放一个皇后,在这个前提下,就可以继续向下判断:
<1 假设我们把第一个皇后放在第一行的第一格,
<2 然后要去放第二个皇后在第二行,我们就要从左到右依次去判断这第二个皇后的首个符合要求的位置
<3 安顿好第二个皇后之后,按照第二个皇后的方式在第三行寻找第三个皇后合适的位置
<4 往后依次执行,直到八个皇后都安顿好位置,一个正确解就诞生了。
<5 当得到这个正确解的时候,回溯算法的思想就要体现出来了,我们会通过回溯法,将八皇依次后从后往前遍历,直到得到所有解。
关键思想:
此题的关键思路就是简化条件,利用行列都有的二维数组解题较为复杂,利用不能在一行的条件将问题化简为不能在同一列和同一斜线的一维数组问题。
当判断是否为一条斜线时,关键思路就是利用斜率,当斜率为1时,表明在同一斜线,此时就不合法。
回溯的思想体现在当得出第一个正确解的时候,退出当前执行的check方法,回到上一个check方法继续执行for循环往后放棋子,直到这一行都完成放置,此时会回退到再前一个的check方法,直到第一行都完成放置,方法结束。
实现代码:
public class EightQueen {
int max = 8;
int[] array = new int[max];
int sum = 0;
//定义一维数组和数组范围,将八个皇后不在同一行作为前提,利用一维数组,数组位置表示行数(也就是第几个皇后),数组位置存的值表示在这一行的第几列
public static void main(String[] args) {
EightQueen eq = new EightQueen();
eq.check(0);
int sum = eq.sum;
System.out.println("一共有"+sum+"种放法");
}
//放置函数,一个个的放置皇后,并且通过判断函数对皇后位置合法性进行判断
private void check(int n){
//设置出口,当到第八个皇后时,表明安顿完毕
if(n == max){
print();
return;
}
//对当前的皇后进行八列的顺序置放,并且判断是否符合要求
for(int i = 0; i < max; i++){
array[n] = i;//表明把第n个皇后放在第i列
//然后对这个皇后的位置合法性进行判断
if(judge(n)){//如果判断条件成立,则继续放下一个皇后
check(n+1);
}
}
}
//判断函数,对第n个皇后进行判断
private boolean judge(int n) {
for(int i = 0; i < n; i++){
//array[i] == array[n] 逐个判断第n个皇后与之前的皇后是否在同一列
//Math.abs(n-i) == Math.abs(array[n]-array[i]) 可以理解为判断斜率,如果y-y0 = x-x0的话,就证明斜率为1,处于同一斜线
if(array[i] == array[n] || Math.abs(n-i) == Math.abs(array[n]-array[i])){
return false;
}
}
return true;
}
private void print(){
System.out.print("{");
for(int i = 0; i < array.length; i++)
{
System.out.print(array[i]+" ");
if(i!=array.length-1)
System.out.print(",");
}
System.out.print("}");
System.out.println();
sum++;
}
}