以八皇后为例来复习一下常见的几种搜索优化方法。
讲真,我觉得,位运算优化的八皇后就是一道状压DP…
luoguP1219
朴素算法
八皇后的核心是c[n]数组,表示第n行在第c[n]个位置。
然后枚举一下1~n-1行即可。
另一大关键是如何判断对角线,也很简单,|c[n]-c[x]|=|n-x|
#include <bits/stdc++.h>
using namespace std;
#define MAXN 14
int c[MAXN],cnt,n;
void print(){
for(int i=1;i<=n;++i){
cout<<c[i]<<" ";
}
cout<<endl;
}
bool check(int x){
for(int i=1;i<x;++i){
if((c[x]==c[i])||(c[x]-c[i]==x-i)||c[x]-c[i]==i-x) return false;
}
return true;
}
void dfs(int k){
if(k==n+1) {
++cnt;
if(cnt<=3)print();
return;
}
for(int i=1;i<=n;++i){
c[k]=i;
if(k==1||check(k)) dfs(k+1);
}
}
int main(){
cin>>n;
dfs(1);
cout<<cnt<<endl;
return 0;
}
这样会T一个点。
分支界定优化
不妨多开两个数组,分别维护两个方向对角线的信息。
这样可以减少判断次数。
剪枝优化
考虑这个棋盘是具有对称性的。
因此,对k皇后,
如果k是偶数,那么我们可以只搜索第一行的一半,从而减少运算量。
如果k是奇数,以n=13为例
1)第一行[1,6]与[8,13]是对称的。
2)如果第一行放在第7个上,那么显然对于下一行,1)的分法是适用的,并且由于下一行不能放在第7个上,所以可以这样枚举。
相当于减少了一半枚举数量。
位运算优化
用dp[i][j]表示第i行的状态为j。这里的j是一个整数,其各个位表示的是某一格点是否存皇后。
比如,dp[5][173(10101101)]表示第5行时1、3、4、6、8列已经放过。
用rd,ld,row维护状态的转移。考虑一下,如果两个皇后在同一竖直线,那么显然下一行左边一个位置、右边一个位置与正下的位置都是不合理的。所以状态就可以转移了。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <bitset>
using namespace std;
typedef long long LL;
LL ans,n,upperlim,qwq[14];
void print(){
for(int i=1;i<=n;++i) cout<<qwq[i]<<" ";
cout<<endl;
}
void dfs(LL ld,LL h,LL rd,int f){
if(h!=upperlim){
LL pos=upperlim & ~(ld|h|rd);
while(pos){
LL p=pos&-pos;
pos-=p;
if(ans<=3){
LL tmp=p;
while(tmp) tmp>>=1,qwq[f]++;
dfs((ld+p)<<1,h+p,(rd+p)>>1,f+1);
qwq[f]=0;
}
else dfs((ld+p)<<1,h+p,(rd+p)>>1,f+1);
}
}else {
++ans;
if(ans<=3) print();
}
}
int main() {
cin>>n;
upperlim=(1<<n)-1;
dfs(0,0,0,1);
cout<<ans<<endl;
}
参考:http://blog.csdn.net/hackbuteer1/article/details/6657109
luogu题解区各位神犇