问题描述
n皇后问题是指一个在n*n的国际象棋棋盘上放置n个皇后,使得这n个皇后两两均不在同一行、同一列、同一条对角线上,求合法的方案数。
算法分析
由题意知,限界条件是n个皇后两两均不在同一行、同一列、同一条对角线上,我们设置一个attack二维数组,用来判断是否可以放置皇后,0表示可以放,1表示不可以放。对于在同一行、同一列、同一对角线上的皇后位置均设为1。例如,
对应的代码为
public void update(int[][]arr,int x,int y,int n){
int[] dx = {-1,-1,-1,0,0,1,1,1};
int[] dy = {-1,0,1,-1,1,-1,0,1};
//表示当前放置皇后的位置(x,y),将其置1
arr[x][y]=1;
//注意i的界限和j的界限
for(int i=1;i<=n-1;i++){
for(int j=0;j<8;j++){
int nx = x+i*dx[j];
int ny = y+i*dy[j];
//条件判断
if(nx>=0&&nx<n&&ny>=0&&ny<n)
//在已经放置了皇后的位置基础上,将会产生攻击的位置置1
arr[nx][ny]=1;
}
}
}
从代码中可以看到,这个update更新函数用来更新attack数组,将已经放置了皇后的位置以及会产生攻击的位置全部置1,那么下次遍历的时候这些地方均不能放置皇后。
除了attack数组外,我们还需要设置一个数组queen用来记录放置了皇后的状态,用"."表示没有皇后,"Q"表示放了皇后。
我们已经知道了需要这两个数组,那么肯定需要先初始化数组,也就是最开始没有放置皇后的时候,那么attack数组应该全部为0,queen数组全部为"."。对应的初始化代码为
public void init(int n){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
attack[i][j]=0;
queen[i][j]='.';
}
}
}
初始化数组后,我们要对数组进行每一行每一列的遍历,例如从第0行第0列开始放置皇后,每放置一个皇后,就要将该位置的queen数组填上"Q",并更新attack数组将该位置所产生的不能放置皇后的位置置1。
public void DFS(int k,int n){
//当K递归到n时,遍历完了最后一行,找到了一组解,cnt用来记录有几种解
if(k==n){
print(n);
cnt++;
return;
}
for (int j = 0; j < n; j++) {
//等于0表示这里可以放皇后
if (attack[k][j] == 0) {
//temp数组用来临时保存attack原来的数组,即初始化时的数组
//因为当遍历完最后一行时,我们又要开始从第0行的下一列开始遍历
int[][] temp = new int[n][n];
copy(temp,attack,n);
queen[k][j] = 'Q';
update(attack, k, j, n);
//递归实现第k+1行的遍历
DFS(k + 1, n);
copy(attack, temp, n);
queen[k][j] = '.';
}
}
}
到此我们的主要代码全部完成。
完整代码
import java.util.Scanner;
public class Queen {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int cnt=0;
int[][] attack = new int[n][n];
char[][] queen = new char[n][n];
public static void main(String[] args){
System.out.println("输入皇后的个数:");
Queen queen = new Queen();
queen.init(queen.n);
queen.DFS(0,queen.n);
System.out.println("共有"+queen.cnt+"种解");
}
//初始化
public void init(int n){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
attack[i][j]=0;
queen[i][j]='.';
}
}
}
//打印输出数组
public void print(int n){
System.out.println("解法:");
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
System.out.print(queen[i][j]+"\t");
}
System.out.println();
}
System.out.println();
}
//递归回溯棋盘
public void DFS(int k,int n){
if(k==n){
print(n);
cnt++;
return;
}
for (int j = 0; j < n; j++) {
//可以装入的情况下;
if (attack[k][j] == 0) {
int[][] temp = new int[n][n];
copy(temp,attack,n);
queen[k][j] = 'Q';
update(attack, k, j, n);
DFS(k + 1, n);
copy(attack, temp, n);
queen[k][j] = '.';
}
}
}
//备份数组
public void copy(int[][] a,int[][] b,int n){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
a[i][j]=b[i][j];
}
}
}
//更新数组
public void update(int[][]arr,int x,int y,int n){
int[] dx = {-1,-1,-1,0,0,1,1,1};
int[] dy = {-1,0,1,-1,1,-1,0,1};
arr[x][y]=1;
for(int i=1;i<=n-1;i++){
for(int j=0;j<8;j++){
int nx = x+i*dx[j];
int ny = y+i*dy[j];
if(nx>=0&&nx<n&&ny>=0&&ny<n)
arr[nx][ny]=1;
}
}
}
}