八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即:任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
思路分析
1)第一个皇后先放第一行第一列
2)第二个皇后放在第二行第一列、然后判断是否OK, 如果不OK,继续放在第二列、第三列、依次把所有列都放完,找到一个合适
3)继续第三个皇后,还是第一列、第二列……直到第8个皇后也能放在一个不冲突的位置,算是找到了一个正确解
4)当得到一个正确解时,在栈回退到上一个栈时,就会开始回溯,即将第一个皇后,放到第一列的所有正确解,全部得到.
5)然后回头继续第一个皇后放第二列,后面继续循环执行 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列
代码实现:
package com.sanjin.study;
/**
* @author sanjin
* @create 2021-12-21 14:21
*/
public class Queue8 {
//定义一个max表示有多少皇后
int max = 8;
//统计有多少种解法
static int count = 0;
//定义数组array,保存皇后放置位置的结果,如arr[8] = {0 , 4, 7, 5, 2, 6, 1, 3}
int[] array = new int[max];
public static void main(String[] args) {
Queue8 queue8 = new Queue8();
queue8.check(0);
System.out.println("一种有" + count + "解法");
}
//编写放置第n个皇后的方法
private void check(int n){
if (n == max){//此时要放第9个皇后,即代表前面8个皇后已经放好了
print();
}
//依次放入皇后,并判断是否冲突
for (int i = 0; i < max; i++) {
//先把当前这个皇后n,放在该行的第1列
array[n] = i;
//判断放置第n个皇后到i列时,是否冲突
if (judge(n)){//不冲突
//接着放第n+1个皇后,即开始递归
check(n + 1);
}
//如果冲突,就继续执行array[n]=i,即将第n个皇后放置在本行的后移一位
}
}
//当放置第n个皇后时,查看是否与前面已摆放的皇后冲突
private boolean judge(int n){
for (int i = 0; i < n; i++) {
//1.array[i] == array[n]判断第n个皇后是否跟前面n-1个在同一列
//2.Math.abs(n - 1) == Math.abs(array[n] - array[i])判断第n个皇后是否跟前面n-1个在同一斜线
//3.n每次都在递增,没必要判断是否在同一行
if (array[i] == array[n] || Math.abs(n - i) == Math.abs(array[n] - array[i])){
return false;
}
}
return true;
}
//写一个方法,可以将皇后摆放的位置打印出来
private void print(){
count++;
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + "\t");
}
System.out.println();
}
}
不得不说这个思路蛮好理解的,就类似于穷举,第一个放在第一列的所有情况列出来,再放到第二列,再列出所有情况,这样循环下去。
但是这代码我是真的难理解,感觉时间复杂度不低,需要判断的次数也很多,而且老师用的是一维数组表示棋盘,在理解上也有一些难。
困了我先趴会,醒了之后找一找有没有其他老师的解法。
俺回来了。
讲真的,自己敲了两个小时都没把二维数组实现敲明白,思路自己都有,就是不知道怎么用代码表达,而且定义了很多变量,像什么i,j,row,col,搞得我都有点混了。
最终,请教了我亲爱的鸭子哥。我奇臭无比的代码,经他手以后,散发出理性的光芒。
代码附上:
package com.sanjin.exer;
/**
* @author sanjin
* @create 2021-12-21 15:48
*/
public class Queue8 {
int queueNum = 8;//皇后的数量
int[][] array = new int[queueNum][queueNum];//创建8*8的棋盘
static int COUNT = 0;//解決方案的个数
public static void main(String[] args) {
Queue8 queue8 = new Queue8();
queue8.put(0);
System.out.println("解决方法一共有" + COUNT + "种");
}
//放置皇后,并判断
public void put(int column){
if (column == queueNum){
COUNT++;
show();
return;
}
for (int i = 0; i < queueNum; i++) {
if (check(i,column)){
array[i][column] = 1;
put(column + 1);
array[i][column] = 0;
}
}
}
/**判断当前位置是否能放皇后
* @param row 行数
* @param col 列数
* @return 能否放置
*/
public boolean check(int row,int col){
for (int i = 0;i < queueNum;i++){
for (int j = 0; j < queueNum; j++) {
if (row == i && col == j){
continue;
}
//同一列有皇后
if (array[i][col] == 1){
return false;
}
//同一行有皇后
if (array[row][j] == 1){
return false;
}
//对角线有皇后
if (Math.abs(row - i) == Math.abs(col - j) && array[i][j] == 1){
return false;
}
}
}
return true;
}
//打印棋盘
public void show(){
System.out.println("第" + COUNT + "方法为:");
for (int i = 0; i < queueNum; i++) {
for (int j = 0; j < queueNum; j++) {
System.out.print(array[i][j] + "\t");
}
System.out.println();
}
}
}
输出结果图:
不仅如此,鸭子哥对我进行了谆谆教导
我拿到跑成功的代码以后,copy到word里面打印下来,自己划了两个小时,还是没看明白。
比如我不懂check()里面为啥要判断row==i&&col==j
我不懂put()里面的colu是啥意思
我不懂put()里面为啥要array[i][colum]=0
给我整emo了,很希望得到解答。等等再找找视频,看看别人的思路吧。
鸭子哥仍给予我厚望:
并且嘱托我:
OK,我继续emo,找视频,看思路。
希望有一天,我可以做出来鸭子哥给我的思考题。
最后再bb一句,我打算听鸭子哥的话了
so明天开始继续赶进度,毕竟我只看了遍java基础,也就只是看了一遍。