位运算的效率要比普通运算快的多,因此我们采用位运算处理数据效率会提高很多。时间复杂度同样是n的n次方,普通方式计算n=14是会用2000ms,而位运算只需300ms。
位运算:
sum=1<<n-1——所有皇后全都放完的状态(例如:n=3,sum为0111,表示三个位置已经全部都用皇后)
1、预处理我们将能放置皇后的地方标为0,不能的标为1。(按照要求看是否需要预处理)
2、dfs(int c,int z,int y,int r)c判断皇后是否放完,z,y处理两条对角线,r为当前行
3、判断所有皇后是否已经放完(即c==sum)。
4、找取第r行的状态,将四者或即为当前行的状态。
5、从第r行开始找0的位置,这里我们将状态按位取反,即查找1的位置(lowbit)。
6、处理下一行。
7、处理下一行时c加上上一行放置皇后时的位置,两个对角线分别z左移1位,y右移一位,r+1。
/*位运算*/
#include<bits/stdc++.h>
using namespace std;
int lowbit(int x)//寻找第一个1的位置
{
return x&-x;
}
int jud[2002],n;
int sum;
int ans;
//void dfs(int c,int z,int y,int r)
void dfs(int c,int z,int y,int r)//c处理列,z,y处理两条对角线,r为当前行的状态
{
if(c==sum){ans++;return;}
int pos=sum&~(c|z|y|jud[r]),p;
while(pos)
{
p=lowbit(pos);
pos-=p;
dfs(c+p,(z+p)<<1,(y+p)>>1,r+1);
}
}
int main()
{
//int n;
time_t st,en;
cin>>n;
//cout<<sum<<endl;
sum=(1<<n)-1;
/*char ch[202];
for(int a=1;a<=n;a++)
{
scanf("%s",ch+1);
getchar();
for(int b=1;b<=n;b++)
if(ch[b]=='.')jud[a]|=(1<<(n-b));
}*/
st=clock();
dfs(0,0,0,1);
en=clock();
cout<<ans<<endl;
cout<<(double)(en-st)<<endl;
return 0;
}
/*常规方式*/
#include <bits/stdc++.h>
using namespace std;
//不能在同一行,同一列,同一条对角线
//求一共有种情况
const int N=100;
int total;
int n;
int a[N],b[N],c[N],d[N];
void print()
{
if(total<=2)//保证只输出前三个解,如果解超出三个就不在输出,但后面的total还需要继续
{
for(int k=1; k<=n; k++)
{
cout<<a[k]<<" ";
}
cout <<endl;
}
total++;
}
void queen(int i)
{//搜索与回溯主体
if(i>n)
{
//print();//输出函数,自己写
total++;
return ;
}else
{
for(int j=1;j<=n;j++)//尝试可能的位置
{
if((!b[j])&&(!c[i+j])&&(!d[i-j+n]))//如果没有被占领
{
a[i]=j;//标记i排是第j个
b[j]=i;//宣布占领
c[i+j]=1;
d[i-j+n]=1;
queen(i+1);//进行下一步搜索,下一个皇后
b[j]=0;
c[i+j]=0;
d[i-j+n]=0;
//回溯
}
}
}
}
int main()
{
time_t st,en;
cin>>n;
st=clock();
queen(1);
en=clock();
cout<<total<<endl;
cout<<(double)(en-st)<<endl;
return 0;
}