题目描述
八皇后问题,一个古老而著名的问题,是回溯算法的典型案例。该问题由国际西洋棋棋手马克斯·贝瑟尔于 1848 年提出:在 8×8 格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。高斯认为有 76 种方案。1854 年在柏林的象棋杂志上不同的作者发表了 40 种不同的解,后来有人用图论的方法解出 92 种结果。计算机发明后,有多种计算机语言可以编程解决此问题。
思路
1. 确定数据结构
- 初看题意,8*8的棋盘,果断用二维数组!定义落子的地方将二维数组的坐标改变即可。
- 使用二维数组固然是没有任何问题,但是却可以进行简化。因为没两个皇后不能再同一行或者同一列,那么,我们以一维数组的下标来记录行数,以一位数组的下标所在的值记录该行皇后所在列数,那么 index - value 就可以表现出皇后的坐标
- 综上所述,我们使用一维数组来记录皇后位置,以index记录第几个皇后,在第几行(默认第几个皇后就在第几行),该下标所在的值记录皇后所在的列数,以index - value记录皇后的坐标
2. 具体实现思路
- 首先,我们定义一个可以将一维数组打印出来的方法:
print(数组)- 其次,我们来判断当前第n个皇后与从第0个皇后开始之间的所有皇后是否处于同一行、同一列、同一斜线。
2.1 同一行不需要判断,因为默认一个皇后一行,不可能同行
2.2 判断是否为同一个斜线 因为棋盘是正方形棋盘,那么当两个皇后的横坐标与纵坐标相减相等时,其与他们的相交点会组成一个等腰直角三角形,此时,他们处于同一斜线上:如下图
- 之后就是将皇后棋落子,从0开始循环到8,给第n个皇后分别赋值为i(i为当前循环到第几个),之后进行判断,是否能落子,如果能落子,那么将n+1,继续落子,即递归调用。
- 回溯 当第n个子不满足时,该方法结束,返回调用它的方法,这时,调用它的方法会进入其方法体内的for循环,将此皇后换一个位置落,若再不行,则又会回溯。
代码
java版本
public class Queen8 {
private static int[] array = new int[8];
private static int count = 0;
public static void main(String[] args) {
Queen8 queen8 = new Queen8();
queen8.put(0);
System.out.println(count);
}
private void put(int n){
if (n == 8){
print();
count++;
return;
}
for (int i = 0; i < 8; i++){
array[n] = i;
if (check(n)){
put(n + 1);
}
}
}
/**
* 校验是否能落子
* @param n 第n+1个皇后
* @return 返回true 能落子 false 不能落子
*/
private boolean check(int n){
for (int i = 0; i < n; i++){
if (array[i] == array[n] || Math.abs(array[i] - array[n]) == Math.abs(i - n)){
return false;
}
}
return true;
}
/**
* 打印数组
*/
private void print(){
for (int i = 0; i < array.length; i++){
System.out.print(array[i]+"\t");
}
System.out.println();
}
}
Go版本
package main
import (
"fmt"
)
//八皇后问题
//记录打印总次数
var count int = 0
//使用一维数组表示棋盘 第i个元素表示第i个皇后,第i行,值表示该皇后在第几列
var data []int = make([]int, 8)
func main(){
put(0)
fmt.Println(count)
}
//思路: 首先,需要有一个放置皇后的方法
func put(n int){
//当放置的n为8时,即为第9个皇后 因为数组从0开始
if (n == 8){
fmt.Println(data)
count++
return
}
//如果不是第9个皇后,那么就遍历来放置第1到8个皇后
for i := 0; i < 8; i ++{
//先把当前的子n 放在i这个位置上
data[n] = i
//判断该点是否可以进行落子
if (check(n)){
put(n + 1)
}
}
}
//检查皇后是否在同一列或者在同一斜线
func check(n int) bool{
for i := 0; i < n; i++{
//第一个 data[i] == data[n] 我们此处用下标代表第几行 用数值代表第几列 那么当第i行的皇后与第n行的皇后在同一列
//就不符合题意了
//第二个 abs(data[n] - data[i]) == abs(i - n) n 和 i代表皇后的横坐标 data[n] data[i]代表皇后的纵坐标 那么两个皇后横坐标相减 纵坐标相减相同时
//其两个皇后构成了一个与棋盘45°的夹角,棋盘是正方形,所以 此时两个皇后处在同一斜线上
if (data[i] == data[n] || abs(data[n] - data[i]) == abs(i - n)){
return false
}
}
return true
}
//goLang没有整数的取绝对值方法 自己写一个
func abs(n int) int{
if (n < 0){
return -n
}
return n
}
天行健,君子以自強不息,地勢坤,君子以厚德载物。