题目描述
给定一个n*n的棋盘,棋盘中有一些位置不能放皇后。现在要向棋盘中放入n个黑皇后和n个白皇后,使任意的两个黑皇后都不在同一行、同一列或同一条对角线上,任意的两个白皇后都不在同一行、同一列或同一条对角线上。问总共有多少种放法?n小于等于8。
输入
输入的第一行为一个整数n,表示棋盘的大小。
接下来n行,每行n个0或1的整数,如果一个整数为1,表示对应的位置可以放皇后,如果一个整数为0,表示对应的位置不可以放皇后。
输出
输出一个整数,表示总共有多少种放法。
样例输入
4
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
样例输出
2
思路一:和n皇后问题一样,可以看成全排列生成问题,直观一点的叙述则为采用递归回溯。
首先用vi数组存放棋盘格子
有白黑皇后,先放哪个都一样。
若先放白,只需检查有无行类对角线冲突和vi数组是否为1;
放完白再放黑,需检查有无行列对角线冲突和vi数组是否为1以及该格子有没有放白
import java.util.Scanner;
public class Main {
static int n;
static int tot=0;
static int[] white=new int[8]; //该行白皇后放在哪一列
static int[] black=new int[8]; //该行黑皇后放在哪一列
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
int[][] vi=new int[n][n];
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
vi[i][j]=sc.nextInt();
whiteEen(vi,0);
System.out.println(tot);
}
public static void whiteEen(int[][] vi,int cur){
if(cur==n) blackEen(vi,0);
else
for(int i=0;i<n;i++){ //枚举列 cur行 i列
if(vi[cur][i]==0)
continue;
else if(check_w(cur,i)){
white[cur]=i;
whiteEen(vi,cur+1);
}
}
}
public static void blackEen(int[][] vi,int cur){
if(cur==n) tot++;
else
for(int i=0;i<n;i++){
if(vi[cur][i]==0 || white[cur]==i)
continue;
else if(check_b(cur,i)){
black[cur]=i;
blackEen(vi,cur+1);
}
}
}
public static boolean check_w(int x,int y){
for(int k=0;k<x;k++)
if( y==white[k] ||Math.abs(x-k)==Math.abs(y-white[k]) ) //white[k]-k==y-x || white[k]+k==y+x 可替换Math.abs(x-k)==Math.abs(y-white[k])
return false;
return true;
}
public static boolean check_b(int x,int y){
for(int k=0;k<x;k++)
if( y==black[k] ||Math.abs(x-k)==Math.abs(y-black[k]) ) //black[k]-k==y-x || black[k]+k==y+x 同上
return false;
return true;
}
}
思路二:
典型dfs回溯,思路同上,只是用dfs改写
import java.util.Scanner;
public class Main {
static int cnt = 0; //计数变量
static int n =0;
static int[][] vis= new int[8][8];
static int[] white=new int[8]; //放在哪一列
static int[] black=new int[8]; //放在哪一列
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
n= in.nextInt();
//0表示不能放皇后,1表示可以放皇后,2表示已经放置了白皇后,3表示已经放置了黑皇后
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
vis[i][j]=in.nextInt();
dfs(0);
System.out.println(cnt);
}
static void dfs(int cur)
{
if(cur==n) {
cnt++;
return ; //
}
else
for(int i=0;i<n;i++){ //试放白皇后
if(judge(cur,i,2)){
vis[cur][i]=2; //一行一行地放置白皇后
white[cur]=i; //cur=x i=y
for(int j=0;j<n;j++){ // //试放黑皇后
if(judge(cur,j,3)){
vis[cur][j]=3; //一行一行地放置黑皇后
black[cur]=j;
dfs(cur+1);
vis[cur][j]=1; //回溯
}
}
vis[cur][i]=1; //回溯 ; white & black 数组为何无需回溯,因为遍历从0至cur
}
}
}
static boolean judge(int x,int y,int state) //state为2时,函数判定白皇后,否则是黑皇后
{
if(vis[x][y]!=1) return false;
if(state==2)
for(int k=0;k<x;k++){ //检查同列 k x
if(vis[k][y]==state || white[k]-k==y-x || white[k]+k==y+x)
return false; //white[k]-k==y-x 一开始y误写成white[x],其实white[x]还未确定或用的是之前一波的数据
}
if(state==3)
for(int k=0;k<x;k++){ //检查同列 k x
if(vis[k][y]==state || black[k]-k==y-x || black[k]+k==y+x)
return false;
}
return true;
}
}