问题描述
给定一个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
解题思路:首先使用二维数组建立棋盘,在分别定义数组w[maxn]存储白皇后落子情况,b[maxn]存储黑皇后落子情况。例如w[3]=1意为白皇后在第三行的落子位置为第一列。逐行递归的落子,每次判断当前落子位置所在的竖列,对角线上是否已有落子,若有则条件不成立,无法落子,进入下一列;若条件成立,则进入下一行,直至完成棋盘遍历,确定最后结果。
对角线的计算方式:数学中的(x1-x2)/(y1-y2)计算斜率,计算结果为1或-1时,对角线倾斜45度/135度,代码中abs();函数作用为计算绝对值
#include<cstdio>
#include<cstring>
#include<cmath>
#define maxn 10
int chess[maxn][maxn];//棋盘
int w[maxn],b[maxn],n,result=0;//w[maxn]存储白皇后落子情况,b[maxn]存储黑皇后落子情况
/*检查是否符合落子规则*/
bool check(char c,int x){
char color=c;
int row=x;
//若为白皇后,则检查其所在横排、竖列或对角线上是否有同色落子
if(color=='w'){
for(int i=1;i<row;i++){
if(w[i]==w[row]||abs(w[i]-w[row])==abs(i-row)){
return false;
}
}
}
//若为黑皇后,则首先检查该位置上是否已经有白皇后,再检查其所在横排、竖列或对角线上是否有同色落子
else{
for(int i=1;i<row;i++){
if(b[i]==b[row]||abs(i-row)==abs(b[i]-b[row])){
return false;
}
}
}
return true;
}
/*黑皇后落子(后)*/
queen_b(int row){
int x=row;
if(x>n){
//满足题意,result值+1
result++;
}
for(int i=1;i<=n;i++){
if(chess[x][i]==1&&w[x]!=i){
b[x]=i;
//检查结果若为真,则落子
if(check('b',x)){
queen_b(x+1);
}
}
}
}
/*白皇后落子(先)*/
queen_w(int row){
int x=row;
//白皇后落满,黑黄后开始落子
if(x>n){
queen_b(1);
}
for(int i=1;i<=n;i++){
if(chess[x][i]==1){
w[x]=i;
//检查结果若为真,则落子
if(check('w',x)){
queen_w(x+1);
}
}
}
}
int main(){
memset(w,0,sizeof(w));
memset(b,0,sizeof(b));
memset(chess,0,sizeof(chess));
scanf("%d",&n);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&chess[i][j]);
}
}
//白皇后先落子,将对应的横行作为参数传递给函数
queen_w(1);
printf("%d",result);
return 0;
}