回溯算法-八皇后
今天学习了下回溯算法,顺便看了下经典案例:八皇后问题。
该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
回溯算法的搜索逻辑是深度优先,即,从一条路往前走,能进则进,不能进则退回来,换一条路再试。
先直接晒代码:
/**
* Created by cxx on 2017/6/20.
*/
public class MyQueen {
//搜索过程中动态产生
//解空间最小单元
private int[] result;
private int num = 1;
//定义皇后个数(可以是9皇后,10皇后。。。)
private int queens;
public MyQueen(int queens){
this.queens = queens;
result = new int[this.queens];
}
//遍历-深度优先
public void backTrack(int i){
if (i < this.queens){
for (int j = 0 ; j < this.queens; j++){
if (i==0 || isKilled(i,j)){
result[i] = j;
backTrack(i+1);
}
}
}else{
showResult();
}
}
//游戏规则->转成数学问题->程序
public boolean isKilled(int i , int j){
for (int k=i-1; k >=0 ; k --){
if (result[k] == (k+(j-i)) || result[k] == ((j+i)-k) || j == result[k]) {
return false;
}
}
return true;
}
//输出解空间
public void showResult(){
System.out.println("\n 解:"+num);
for (int i = 0 ; i < this.queens ; i++){
for (int j = 0 ; j < this.queens ; j++){
if (result[i] == j ){
System.out.print("*");
}else {
System.out.print(".");
}
}
System.out.println();
}
num++;
}
public static void main(String[] args) {
MyQueen myQueen = new MyQueen(8);
long c = System.nanoTime();
myQueen.backTrack(0);
System.out.println("end:"+(System.nanoTime()-c)/1000);
}
}
首先定义解空间:int[] result。
使用int[]简化输出结果集,存储着每行皇后所处位置的下标。result[0] = 5 表示第一行皇后位于索引下表为5的位置(.....*..);
然后开始遍历,目的是组织解空间,即,给result赋值。
逐行遍历,找到该行的皇后后,立即动身寻找下一行的皇后,以先找出一个完整的result为优先(深度优先)。凑不出一个result就舍弃(每个result的i必须凑到8,不足的则舍弃)。找到后回来继续遍历。
遍历的筛选条件从题目中来,转化成数学模型即三个简单的函数:
y=x+k(正斜线)
y=-x+k(反斜线)
y=k(竖线)
由于横向遍历,所以无需考虑会在同一条横线上的情况。满足三个函数任意,即皇后可互相攻击,就舍弃。
运行结果:
解:1
*.......
....*...
.......*
.....*..
..*.....
......*.
.*......
...*....
解:2
*.......
.....*..
.......*
..*.....
......*.
...*....
.*......
....*...
解:3
*.......
......*.
...*....
.....*..
.......*
.*......
....*...
..*.....
这里没有贴出所以的输出,一共是92个。感兴趣的可以自己运行一下试试。该文章是学习笔记,有问题的话欢迎指出。