八皇后问题是一个古老而著名的问题,是回溯算法的典型例题。该问题是十九世纪著名的数学家高斯1850年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。
简单介绍回溯法思路,就是将所有的结果变成一棵树,从树的结点开始访问,采用深度优先策略,从树的根结点开始访问,如果满足条件,继续访问下一层,如果不满足条件,返回上一个结点,继续访问其它结点。重复操作。
首先,可归纳问题的条件为,8皇后之间需满足:
1.不在同一行上
2.不在同一列上
3.不在同一斜线上
4.不在同一反斜线上
代码如下:
class Eight{
//n皇后如何处理?n>=4
public static int count=0;
public static int n;
public static void main(String[] args){
n=8;
int[][] board=new int[n][n];
//0就是空 1就是皇后
eightQueen(board,0);
}
//解决board在第level层的八皇后问题 level 0~7
public static void eightQueen(int[][] board,int level){
if(level==n){ //如果递归到了第9行 则当前是一个解
count++;
System.out.printf("这是第%d个解:\n",count);
for(int i=0;i<board.length;i++){
for(int j=0;j<board[i].length;j++){
System.out.print(board[i][j]+" ");
}
System.out.println();
}
}else{
//1.先做一份上一个情况的备份
int[][] newBoard=new int[n][n];
for(int i=0;i<board.length;i++){
for(int j=0;j<board[i].length;j++){
newBoard[i][j]=board[i][j];
}
}
//2.遍历当前行 找到所有可能的解
for(int col=0;col<n;col++){
//3.判断当前位置是否可以放皇后
// 3.1 如果可以 则继续往下一行去寻找
// 3.2 如果不行 则继续判断一下个位置
if(isNoDanger(newBoard,level,col)){
//在赋值皇后之前 先当前行清空
for(int c=0;c<n;c++){
newBoard[level][c]=0;
}
newBoard[level][col]=1; //当前给皇后
eightQueen(newBoard,level+1);//接着下一行找
}
}
}
}
//判断board当中 x y的位置上是否可以放置一个皇后
public static boolean isNoDanger(int[][] board,int x,int y){
//正上
for(int r=x-1;r>=0;r--){
if(board[r][y]==1){
return false;
}
}
//左上
for(int r=x-1,c=y-1;r>=0&&c>=0;r--,c--){
if(board[r][c]==1){
return false;
}
}
//右上
for(int r=x-1,c=y+1;r>=0&&c<n;r--,c++){
if(board[r][c]==1){
return false;
}
}
return true;
}
}
总结:其实感觉回溯法有类似穷举,把所有的结果都列出来。只是回溯法,把结果变成了一棵树,如果在某个结点的时候,已经不满足条件了,那后面的选择肯定不满足条件,这样就节省了遍历所有的结果的时间。提高了效率。