八皇后问题
八皇后问题
八皇后问题(英文:Eight queens),是由国际西洋棋棋手马克斯·贝瑟尔于1848年提出的问题,是回溯算法的典型案例。
问题表述为:在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。高斯认为有76种方案。
回溯算法
定义
回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法。
八皇后问题就是回溯算法的典型,第一步按照顺序放一个皇后,然后第二步符合要求放第2个皇后,如果没有位置符合要求,那么就要改变第一个皇后的位置,重新放第2个皇后的位置,直到找到符合条件的位置就可以了。
回溯在迷宫搜索中使用很常见,就是这条路走不通,然后返回前一个路口,继续下一条路。回溯算法说白了就是穷举法。
八皇后问题如果用穷举法需要尝试88=16,777,216种情况。每一列放一个皇后,可以放在第 1 行,第 2 行,……,直到第8行。
回溯算法优于穷举法,其求解八皇后问题的原则是:有冲突解决冲突,没有冲突往前走,无路可走往回退,走到最后是答案。
八皇后问题解答
- 第一个皇后先放第一行第一列
- 第二个皇后放在第二行第一列、然后判断是否 OK, 如果不 OK,继续放在第二列、第三列、依次把所有列都放完,找到一个合适
- 继续第三个皇后,还是第一列、第二列……直到第 8 个皇后也能放在一个不冲突的位置,算是找到了一个正确解
- 当得到一个正确解时,在栈回退到上一个栈时,就会开始回溯,即将第一个皇后,放到第一列的所有正确解, 全部得到.
- 然后回头继续第一个皇后放第二列,后面继续循环执行 1,2,3,4 的步骤
说明:
理论上应该创建一个二维数组来表示棋盘,但是实际上可以通过算法,用一个一维数组即可解决问题. arr[8] =
{0 , 4, 7, 5, 2, 6, 1, 3} 对应 arr 下标 表示第几行,即第几个皇后,arr[i] = val , val 表示第 i+1 个皇后,放在第 i+1行的第 val+1 列。
代码实现
/**
* 八皇后问题
*/
public class Queen8 {
//定义一个max,表示一共有几个皇后
static int max = 8;
static int count = 0;
//定义数组array,保存皇后放置位置的结果,比如arr= {0,4,7,5,2,6,1,3}
static int[] array = new int[max];
public static void main(String[] args) {
//放置第0个
check(0);
System.out.println("一共有"+count+"种解法");
}
//放置第n个皇后
private static void check(int n){
if(n == max){ //8个皇后已经放好
print();
return;
}
//一次放入皇后并判断是否冲突
for (int i = 0; i < max; i++) {
//先把当前的皇后(第n个),放到该行的第一列
array[n] = i;
//判断当放置第n个皇后到i列是时候冲突
if(judge(n)){
//接着放第n+1个皇后
check(n+1);
}
//如果冲突就继续执行array[n]=i;即将第n个皇后,放在本行的下一个位置
}
}
/**
*
* @param n 表示第n个皇后
* @return
*/
private static boolean judge(int n){
for (int i = 0; i < n; i++) {
//array[i]==array[n] 判断时候在同一列
//Math.abs(n-i)==Math.abs(array[n] - array[i]) 判断是否是同一斜线
// n = 1放置第2列1 n = 1 array[1] = 1
//Math.abs(1-0) == 1 Math.abs(array[n] - array[i]) = Math.abs(1-0) = 1
//判断是否在同一行,没有必要,n每次都在递增
if(array[i]==array[n] || Math.abs(n-i) == Math.abs(array[n] - array[i])) {
return false;
}
}
return true;
}
//写一个方法,输出皇后摆放的位置
private static void print(){
count++;
for (int i = 0; i < array.length; i++) {
System.out.print((array[i]+1)+" ");
}
System.out.println();
}
}
代码分析:
可能看完代码大家会有些迷糊,我就在这里分析一下,比较难的代码地方,
首先要明确数组的索引值+1(下标)是第几行,数组中存放的值+1为第几列。就相当于坐标轴x,y的关系。
在对皇后位置判断的地方:
array[i]==array[n]
这个就是判断数组值是不是一样,一样的话就是在一列中,即判断两个坐标(x,y)中y值是否相等
Math.abs(n-i) == Math.abs(array[n] - array[i])
上述代码是为了判断相邻的两个皇后是否在同一个斜线上,即可以理解为有两个坐标(x1,y1)(x2,y2)
(x1-x2)的绝对值等于(y1-y2)的绝对值,如图
运行结果:
一共有92中解法。
本文地址:https://blog.csdn.net/Pluto372/article/details/112547105