题目描述
有一个N*N的棋盘,有些格子可以放置皇后,一个皇后可以对它这一行的位置,这一列的位置,它所在的左对角线和右对角线攻击,这些位置不能放置皇后,一共放置N个皇后,问有多少种放置的方式。
输入输出格式
输入格式:
第一行有一个N。接下来有N行N列描述一个棋盘,“*”表示可放“.”表示不可放。
输出格式:
输出方案总数
solution
这道题主要考察位运算,例如101001表示这一排第2、4、5个位置可以放置皇后。
我们用k进制(这里是二进制)来存储状态,这也是状态压缩的基本思想。
显然每行只能放置一个皇后,我们逐行枚举
那么我们需要记录四个状态,一是这一行题目给定的状态,二是这一行中有哪些列会被之前摆放的皇后攻击,那么无法再摆放,三是左对角线的影响,四是右对角线的影响,这些状态只用每行一个数字的二进制来存储就行了。
判断时将这四种情况取并集,再取反,1的位置就成了可以放置的位置,利用lowbit进行DFS即可,具体的实现细节还要看代码
code
#include<cstdio> #include<iostream> #include<string> #define re register using namespace std; int n,all,sta[50],ans; string s; int lowbit(int x)//找到最右边的1位 { return x&-x; } //取反是补码,与反码不同 ~(3)=-4; void dfs(int now,int L,int R,int d) { if(now==all){ans++;return;} //当now==all的时候,说明全部摆放完毕,ans++ //now表示到这一行的摆放情况,L表示左对角线的情况 //R表示右对角线的情况,d记录现在在哪一行, //sta[d]表示第d行题目的限制条件 int pos=all&(~(now|L|R|sta[d])),p; //all是用来限制,把右对角线左移移出去的除掉 while(pos)//枚举每个可以放的位置 { p=lowbit(pos); pos-=p;//这一个位置即将枚举,删掉 dfs(now+p,(L+p)>>1,(R+p)<<1,d+1);//左对角线对下一行的影响是它的左下角,因此二进制右移 //右对角线相反,别忘了加上p处放置的棋子的影响 } } int main() { scanf("%d",&n); all=(1<<n)-1;//表示最终状态,比如 n=4 01111 for(re int i=1;i<=n;++i) { cin>>s; for(int j=1;j<=n;++j) if(s[j-1]=='.')sta[i]|=(1<<(n-j));//对这个数二进制每个位置进行操作 } dfs(0,0,0,1);//一开始没放,没有任何摆放过皇后限制 printf("%d",ans); return 0; }