<题目链接>
题目描述
给定一个n*n的棋盘,棋盘中有一些位置不能放皇后。现在要向棋盘中放入n个黑皇后和n个白皇后,使任意的两个黑皇后都不在同一行、同一列或同一条对角线上,任意的两个白皇后都不在同一行、同一列或同一条对角线上。问总共有多少种放法?n小于等于8。
输入
输入的第一行为一个整数n,表示棋盘的大小。
接下来n行,每行n个0或1的整数,如果一个整数为1,表示对应的位置可以放皇后,如果一个整数为0,表示对应的位置不可以放皇后。
接下来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皇后问题十分类似,也是利用递归回溯求解,因为每行只能放一个白皇后,所以可以用一维数组记录皇后所放的位置,如 qw[x]=i,表明白皇后放在第x行第i列(黑皇后类似)。这题放2n个皇后,我采取的做法是,先放n个黑皇后,再放n个白皇后,具体实现见代码,一些细节方面我都标注出来了,并且做了详细解释。
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 5 #define clr(a,b) memset(a,b,sizeof(a)) 6 int n,ans; 7 int map[10][10]; 8 int qw[10],qb[10]; //白、黑皇后对应行所在的列数 9 int vis[10][10]; 10 bool juge(int x,int cal[]){ 11 if(!map[x][cal[x]]||vis[x][cal[x]])return false; //如果该点为0或者已经有皇后,则不能放 12 for(int i=1;i<x;i++){ 13 if(cal[i]==cal[x]||(abs(i-x)==abs(cal[i]-cal[x])))return false; //与之前放的同色皇后不在同一列,不在同一对角线 14 } 15 return true; 16 } 17 void init(){ 18 ans=0; 19 clr(vis,0);clr(qw,0);clr(qb,0); 20 } 21 void dfs_w(int x){ //放白皇后 22 if(x==n+1){ 23 ans++; 24 return; 25 } 26 for(int i=1;i<=n;i++){ 27 qw[x]=i; 28 if(juge(x,qw)){ 29 vis[x][i]=1; 30 dfs_w(x+1); 31 vis[x][i]=0; 32 } 33 } 34 } 35 void dfs_b(int x){ //放黑皇后 36 if(x==n+1){ //这里只用判断是不是到达第n+1行就能判断是否放了n个黑皇后,不用另外用一个变量记录放的黑皇后的数量 37 dfs_w(1); //因为如果某一行没有放黑皇后,那么它根本就不能够向下一行搜索 38 return; 39 } 40 for(int i=1;i<=n;i++){ 41 qb[x]=i; //这里先放皇后再判断也是没问题的,因为如果放了皇后之后,发现不符合,这也没关系,因为qb[x]的值会被下一个循环 42 if(juge(x,qb)){ //的i值所覆盖。即使是循环的最后一个值不符合也没有关系,因为如果循环最后一个i不符合的话,那么它根本不会向下递归 43 vis[x][i]=1; //也就自然不会对结果造成影响 44 dfs_b(x+1); 45 vis[x][i]=0; 46 } 47 } 48 } 49 int main(){ 50 init(); 51 scanf("%d",&n); 52 for(int i=1;i<=n;i++) 53 for(int j=1;j<=n;j++) 54 scanf("%d",&map[i][j]); 55 dfs_b(1); 56 printf("%d ",ans); 57 return 0; 58 }
2018-08-26