八皇后问题:有一个八行八列的棋盘,有八个皇后棋子,这八个棋子摆在这八行八列的棋盘上。
而且八个皇后中的任意两个不能处于同一行、同一列,或同一斜线上。
本文地址:https://blog.csdn.net/kaeles/article/details/84933931
- 通过问题我们可以看出,在棋盘上每行每列只能有且只有一个皇后。
- 假如第一颗棋子落在(0,i)点上,那么第二行的棋子落在(1,j)点,其中 j≠i & j≠i-1 & j≠i+1,共有5或6中方法。以此类推。
- 其中第一颗棋子共有8中落点。
解:
- 1、把棋盘放进8*8的二位数组中。
- 2、遍历当前行所有的列,取出点
- 2、判断当前点是否可以落棋子
- 3、如若可落子,回到第2步(通过递归),判断下一行
- 4、所有行都已落子,记录解法并返回。
- 5、继续通过第2步获取下一个点,继续往下执行直到所有列遍历完
private int size = 8; //棋盘行列
private int[][] checkerboard ;//首先把棋盘初始化出来,此处代码:略
private void QueensSolving(int row = 0){
if(row >= size){
AddSolving();//添加当前的解到列表中
return;
}
for(int col = 0 ; col < size ; col++){
if( Check(row , col) ){
checkerboard[row][col] = 1;
QueensSolving( row + 1 );
checkerboard[row][col] = 0;//避免递归时出现藏数据,
}
}
}
private bool Check(int row, int col){
for(int i = 0 , i < size ; i++){
if (queens[qx][i] == 1) return false;//当前行
if (queens[i][qy] == 1) return false;//当前列
//上半部分两边斜角
if (qx - i >= 0) {
if (qy - i >= 0 && queens[qx - i][qy - i] == 1) return false;
if (qy + i < 8 && queens[qx - i][qy + i] == 1) return false;
}
//下半部分两边斜角
if (qx + i < 8) {
if (qy - i >= 0 && queens[qx + i][qy - i] == 1) return false;
if (qy + i < 8 && queens[qx + i][qy + i] == 1) return false;
}
}
}
修改1
- 把QueensSolving(int row = 0)中的列学循环也改成递归方式
private void QueensSolving(int row = 0, int col = 0){
if(col >= size) return;
if(row >= size){
AddSolving();//添加当前的解到列表中
return;
}
if( Check(row , col) ){
checkerboard[row][col] = 1;
QueensSolving( row + 1 ); //col从0开始递归,
checkerboard[row][col] = 0;//避免递归时出现藏数据,
}
QueensSolving( row ,col+1 );
}
优化1
检测函数Check(int row, int col)是用时的消耗大户,在此去掉不必要的判断。
- 1、每行某点通过检测后,会进行落子并前往下一行,所以每仅进且只有一个皇后
- 2、落子是从低位依次往高位,所以当前点是当前解法中出现的最大点,当前解法出现的皇后都比该点低。
通过上面两点,可修改检测函数为:
private bool Check(int row, int col){
for(int i = 0 , i < row; i++){
if (queens[i][col] == 1) return false;//当前列
if (col- i >= 0 && queens[row- i][col- i] == 1) return false;//左斜角
if (col+ i < 8 && queens[row- i][col+ i] == 1) return false;//右斜角
}
}
优化2
- 因为每行有且仅有一个皇后,前文中也是以每行为单位进行计算的,那么把二位数组,改成一维数组,每个元素储存的为当前行皇后所在的列。
- 因为行、列都是已0开始,那么把一维数组初始值设为-1;
- Check(int row, int col)通过取斜边上的点与该点所在行上皇后的点对比
private int[] checkerboard = new int[size];
private void QueensSolving(int row = 0, int col = 0){
if(col >= size) return;
if(row >= size){
AddSolving();//添加当前的解到列表中
return;
}
if( Check(row , col) ){
checkerboard[row] = col;
QueensSolving( row + 1 ); //col从0开始递归,
checkerboard[row] = -1;//避免递归时出现藏数据,
}
QueensSolving( row ,col+1 );
}
private bool Check(int row, int col){
for(int i = 0 , i <= row; i++){
if ( queens[ i ] == col ) return false;//当前列
if ( queens[ row - i ] == col - i ) return false;//左斜角
if ( queens[ row - i ] == col + i ) return false; //右斜角
}
}
- 需要注意以下 i <= row,不然的话计算不到第0行的数据
修改2
- 因为斜边围城的是一个正四边形,所以行之间的差和列之间的差是相同的,例如(2,3)和(5,6),两点同在同一条斜边上,5-2=6-3。
- Check(int row, int col)也可通过行与行,列与列的差值对比
private bool Check(int row, int col){
for(int i = 0 , i < row; i++){
if ( queens[ i ] == col ) return false;//当前列
if ( queens[ i ] - col == row - i ) return false;//左斜角
if ( queens[ i ] - col == i - row ) return false; //右斜角
}
}
- 修改后,一维数组便可不需要设置初始值。
- 此处最好不要用Math.Abs() 太浪费,消耗的时间是普通运算的几十倍