试题 基础练习 2n皇后问题
资源限制
时间限制:1.0s 内存限制:512.0MB
问题描述
给定一个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
样例输入
4
1 0 1 1
1 1 1 1
1 1 1 1
1 1 1 1
样例输出
0
【注意】
①其实后续judge_black和judge_white这两个对黑皇后和白皇后位置关系是否合理的判断方法,是可以合在一起的,在传入参数时多加一个“2–黑”或者“3–白”即可。
②不一定要等到最后一行的黑皇后放好,再对整盘的黑皇后做判断。可以在第二行的时候就每走一步判断一次。
比如说,第一个黑皇后放在(0,0),那么递归后第二行会在(1,0)开始放第二个黑皇后,但是此时两个黑皇后已经在同一列,不满足题意,若先调用judge方法判断,可以免了该情况下第三行和第四行的递归调用,节省了许多运行时间,回溯的时候也会简单一点。
import java.util.Scanner;
public class Main{
static int sum=0;
static int n;
public static void main(String[] args){
Scanner input=new Scanner(System.in);
n = input.nextInt();
int[][] arr = new int[n][n];
int i,j;
//输入棋盘
for(i=0;i<n;i++){
for(j=0;j<n;j++){
arr[i][j] = input.nextInt();
}
}
find_black(arr,0);
System.out.print(sum);
}
//判断黑皇后合理的放法,再调用函数判断白皇后
public static void find_black(int[][] arr,int a){
for(int i=0;i<n;i++){
//遇到0则跳过
if(arr[a][i]==0) continue;
//否则放入黑皇后(2代表黑皇后)
arr[a][i]=2;
//递归,逐层调用find_black使棋盘各行获得一个黑皇后
if(a<n-1) find_black(arr,a+1);
//当到最后一行时
if(a==n-1){
//判断此时n个黑皇后摆放位置是否满足要求
int b=judge_black(arr,n);
//若满足要求
if(b==1){
//调用find_white放入白皇后
find_white(arr,0);
//抹除所有白皇后的痕迹
for(int j=0;j<n;j++){
for(int k=0;k<n;k++){
if(arr[j][k]==3) arr[j][k]=1;
}
}
}
}
//抹除部分黑皇后--即:回溯上一步
for(int j=a;j<n;j++){
for(int k=0;k<n;k++){
if(arr[j][k]==2){
arr[j][k]=1;
break;
}
}
}
}
}
//加入白皇后后,计算合理放法的个数
public static void find_white(int[][] arr,int a){
for(int i=0;i<n;i++){
//若遇到0和黑皇后则跳过
if(arr[a][i]==2 || arr[a][i]==0) continue;
//否则放入白皇后
arr[a][i]=3;
//递归,每行都放入一个白皇后
if(a<n-1) find_white(arr,a+1);
//当放好最后一行时
if(a==n-1){
//判断白皇后的摆放位置是否满足要求
int b=judge_white(arr,n);
//若是,sum++,即方法+1
if(b==1) sum++;
}
//回溯———抹除最后一步白皇后的痕迹
for(int j=a;j<n;j++){
for(int k=0;k<n;k++){
if(arr[j][k]==3) arr[j][k]=1;
}
}
}
}
//判断黑皇后是否全部不在同一行、列、对角线
public static int judge_black(int[][] arr,int n){
int i,j,a=0,b=0;
//行,列
for(i=0;i<n;i++){
for(j=0;j<n;j++){
if(arr[i][j]==2) a++;
if(arr[j][i]==2) b++;
}
if(a>=2 || b>=2) return 0;
a=0;
b=0;
}
//对角线
//将放黑皇后的位置储存在arr1数组中
a=0;
int[][] arr2 = new int[n][2];
for(i=0;i<n;i++){
for(j=0;j<n;j++){
if(arr[i][j]==2){
arr2[a][0]=i;
arr2[a++][1]=j;
}
}
}
//若存在两个黑皇后的行数之差==列数之差,或者行列之和相等,则其处于同一对角线
for(i=0;i<n-1;i++){
for(j=i+1;j<n;j++){
if(arr2[i][0]-arr2[j][0] == arr2[i][1]-arr2[j][1]) return 0;
if(arr2[i][0]+arr2[i][1]-arr2[j][0]-arr2[j][1]==0) return 0;
}
}
//若不在同行、列、对角线,则满足要求,返回1
return 1;
}
//与上面judge_black方法雷同,其实在传参数时引入黑--2、白--3,即可将两个方法合二为一
public static int judge_white(int[][] arr,int n){
int i,j,a=0,b=0;
//行,列
for(i=0;i<n;i++){
for(j=0;j<n;j++){
if(arr[i][j]==3) a++;
if(arr[j][i]==3) b++;
}
if(a>=2 || b>=2) return 0;
a=0;
b=0;
}
//对角线
//将放白皇后的位置储存在arr1数组中
b=0;
int[][] arr3 = new int[n][2];
for(i=0;i<n;i++){
for(j=0;j<n;j++){
if(arr[i][j]==3){
arr3[b][0]=i;
arr3[b++][1]=j;
}
}
}
//若存在两个白皇后的行数之差==列数之差,或者行列之和相等,则其处于同一对角线
for(i=0;i<n-1;i++){
for(j=i+1;j<n;j++){
if(arr3[i][0]-arr3[j][0] == arr3[i][1]-arr3[j][1]) return 0;
if(arr3[i][0]+arr3[i][1]-arr3[j][0]-arr3[j][1]==0) return 0;
}
}
//若不在同行、列、对角线,则满足要求,返回1
return 1;
}
}