深搜本质
本质上深搜是通过自己调用自己的方式搜索所有状态空间,最终确定可行解的过程,值的注意的是状态空间不等于可行解;我理解的状态空间指的是包括了搜索中间结果的所有状态;
深度优先搜索与递归是息息相关的,因为它搜索的过程总是从当前过程选出一个可行的下一个状态继续搜索,直到搜索到可行解为止。
&esmp;所以深搜的过程可以抽象为在图上进行搜索的过程,图中的节点是搜索过程中的中间态,而图中的边则是所有决策的可能情况。
计算机求解问题其实就是在可行域里面不断搜索,探寻可行解的过程,所以搜索可以看做是计算机工作的本质原理,而DFS又是搜索方法里面最常用最普适的方法之一,所以深搜是非常重要的。
剪枝方法
主要的剪枝方法有以下5种:
- 优化搜索顺序
探索解空间的搜索顺序可能有多种,但是合理的搜索顺序会减少后续过程中产生的分枝数目,从而减少搜索空间的大小;比如说数独游戏中,我们首先搜索填充选择性最小的位置,那么这些已经填充的点就会影响后续填充过程,使得很多不合法的填充方式被尽早剔除。 - 排除冗余搜索
排除冗余搜索我认为与记忆化是可以相互结合的,本质上也是一样的,对于一些冗余的搜索过程,我们可以在第一遍搜索时记录,在后续搜索到时避免再次搜索,但是这种剪枝方法只有在有向无环图中才可行,搜索空间是一个树形结构时,我们每一个分支最多只会搜索到一次,所以也就没有必要记录。 - 可行性判定
对于某些问题,题目中显式或者隐式地包含了一些可行性要求,比如说跳跃楼梯,题目中要求只能跳跃1步或者两步,那么这就是一个可行性约束,对于超过2步或者小于1步的操作我们可以直接避免搜索。 - 最优性剪枝
与可行性约束是类似的,不过最优性剪枝是搜索过程中动态形成的约束,当可行解有多个,而我们的目标是选取可行解中某个最优解,那么在第一个可行解搜索到以后,后面搜索到中间过程中发现此时解已经超过最优解的限制,那么显然这个分支不会成为最优解,我们可以提前结束这一分支的搜索。 - 记忆化
记忆化其实是避免冗余搜索的一种手段,我们可以对冗余搜索分支进行记录,以空间换时间,实现间时间复杂度从指数级别减低到多项式级别的目标。但是可以使用记忆化的问题,其搜索空间形成的图结构必须是有向无环图形式的。
例题
优化搜索顺序
#include<bits/stdc++.h>
using namespace std;
char str[100];//优化字符串的输入,就可以AC
int row[9];
int col[9];
int son[9];
bool tag;
//优化搜索顺序;
int num[512];
unordered_map<int,int> has;
inline int lowbit(int x){
return x&(-x);
}
void DFS(){
if(tag)return;
int k=-1;
int state=(1<<9)-1;
for(int i=0;i<81;i++){
if(str[i]=='.'){
if(num[state]>num[(row[i/9]&col[i%9]&son[((i/9)/3)*3+(i%9)/3])]){
state=row[i/9]&col[i%9]&son[((i/9)/3)*3+(i%9)/3];
k=i;
}
}
if(num[state]==1)break;
}
if(k==-1){
tag=true;
printf("%s\n",str);
return;
}
for(int i=state;i;i-=lowbit(i)){
int b=lowbit(i);
row[k/9]^=b;
col[k%9]^=b;
son[(k/9)/3*3+(k%9)/3]^=b;
str[k]=has[b]+'0';
DFS();
row[k/9]^=b;
col[k%9]^=b;
son[((k/9)/3)*3+(k%9)/3]^=b;
}
str[k]='.';
return;
}
int main(){
for(int i=0;i<9;i++){
has[1<<i]=i+1;
}
num[0]=0;
for(int i=1;i<(1<<9);i++)num[i]=num[i-lowbit(i)]+1;
while(true){
scanf("%s",&str);
if(str[0]=='e')break;
memset(row,0,sizeof(row));
memset(col,0,sizeof(col));
memset(son,0,sizeof(son));
tag=false;
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
if(str[i*9+j]!='.'){
int ch=str[i*9+j]-'1';
row[i]|=(1<<ch);
col[j]|=(1<<ch);
son[(i/3)*3+j/3]|=(1<<ch);
}
}
}
int state=(1<<9)-1;
for(int i=0;i<9;i++){
row[i]^=state;
col[i]^=state;
son[i]^=state;
}
DFS();
}
return 0;
}