N皇后问题
8皇后问题
❓ 在 8*8 的棋盘上,摆放八个皇后,使其不能互相攻击:任意两个皇后不能处于同一行、列、对角线上。 问有多少种摆法?
回溯解法
首先,缩小问题范围(4皇后问题),理解回溯的思想:
剪枝:
根据限制条件,发现同一行同一列、对角线不能摆放皇后,可以进行剪枝操作。
package 回溯;
/**
* @ClassName: Quen
* @Description: 八皇后问题
* @author: WangZe
* @date: 2022/3/14 14:40
*/
public class Queens{
public static void main(String[] args) {
new Queens().placeQueens(8);
}
/**
* 数组索引是行号,数组元素是列号,用来记录皇后的位置
*/
int[] cols;
/**
* 有多少中摆法
*/
int ways;
/**
* 思路:回溯+剪枝
* @param queenCount 皇后数量
*/
public void placeQueens(int queenCount){
if(queenCount < 1){return;}
cols = new int[queenCount];
//从第0行开始摆放
place(0);
System.out.println(ways);
}
/**
* 从第row行开始摆放皇后
* @param row 行
*/
public void place(int row){
//如果到row==n 说明已经摆完了
if(row == cols.length){
ways++;
return;
}
for (int col = 0; col < cols.length; col++) {
//如果位置能摆放皇后
if(isValid(row,col)){
//在第row行,第col列摆放皇后
cols[row] = col;
//摆放好后,开始下一行
place(row+1);
}
}
}
/**
* 剪枝函数,判断第row行,第col列是否合法
* @param row
* @param col
* @return
*/
public boolean isValid(int row,int col){
for (int i = 0; i < row; i++) {
//列有皇后
if(cols[i] == col){
return false;
}
//斜线有皇后
//(row - i)/(col-cols[i]) == 1 or -1;斜率为1or-1,代表处于同一斜线
if(row - i == Math.abs(col - cols[i])){
return false;
}
}
return true;
}
}
如何显示具体摆放位置呢???
package 回溯;
/**
* @ClassName: Quen
* @Description: 八皇后问题
* @author: WangZe
* @date: 2022/3/14 14:40
*/
public class Queens{
public static void main(String[] args) {
new Queens().placeQueens(4);
}
/**
* 数组索引是行号,数组元素是列号,用来记录皇后的位置
*/
int[] cols;
/**
* 有多少中摆法
*/
int ways;
/**
* 思路:回溯+剪枝
* @param queenCount 皇后数量
*/
public void placeQueens(int queenCount){
if(queenCount < 1){return;}
cols = new int[queenCount];
//从第0行开始摆放
place(0);
System.out.println(queenCount+"皇后的摆放有几种摆法? "+ways);
}
/**
* 从第row行开始摆放皇后
* @param row 行
*/
public void place(int row){
//如果到row==n 说明已经摆完了
if(row == cols.length){
ways++;
show();
return;
}
for (int col = 0; col < cols.length; col++) {
//如果位置能摆放皇后
if(isValid(row,col)){
//在第row行,第col列摆放皇后
cols[row] = col;
//摆放好后,开始下一行
place(row+1);
}
}
}
/**
* 剪枝函数,判断第row行,第col列是否合法
* @param row
* @param col
* @return
*/
public boolean isValid(int row,int col){
for (int i = 0; i < row; i++) {
//列有皇后
if(cols[i] == col){
return false;
}
//斜线有皇后
//(row - i)/(col-cols[i]) == 1 or -1;斜率为1or-1,代表处于同一斜线
if(row - i == Math.abs(col - cols[i])){
return false;
}
}
return true;
}
/**
* 打印摆法
*/
public void show(){
for (int row = 0; row < cols.length; row++) {
for (int col = 0; col < cols.length; col++) {
if(cols[row] == col){
System.out.print("1 ");
}else {
System.out.print("0 ");
}
}
System.out.println();
}
System.out.println("---------------------");
}
}
回溯的过程分析
在剪枝方法中,添加打印语句,观察整个回溯过程:
public boolean isValid(int row,int col){
for (int i = 0; i < row; i++) {
//列有皇后
if(cols[i] == col){
System.out.println("["+row+"] ["+col+"] = false");
return false;
}
//斜线有皇后
//(row - i)/(col-cols[i]) == 1 or -1;斜率为1or-1,代表处于同一斜线
if(row - i == Math.abs(col - cols[i])){
System.out.println("["+row+"] ["+col+"] = false");
return false;
}
}
System.out.println("["+row+"] ["+col+"] = true");
return true;
}
4皇后其中一次的过程: