深度优先搜索,就是将可能的答案从头到尾进行递归枚举,在确定前一部分的情况下,根据规则往下扩展答案树,直到得到可行的答案
广搜,借助队列实现,扩展节点入队
双向广搜,双向扩展结点,在两个方向的扩展顺序上,可以轮流交替进行,但由于大部分的解答树并不是棵完全树 ,在扩展完一层后,下一层则选择结点个数较少的那个方向先扩展,可以克服两个方向结点生成速度不平衡的状态,明显提高搜索效率
剪枝
1.如果当前节点已经到达过,就可以直接剪掉,这个可以用哈希表来实现,也可以根据情况直接开一个Judge数组
2.使用估价函数判定,如果在最乐观的情况下,所需要的步数仍然不如已经找到的最优解,就直接剪掉
注意事项
1.在传递参数的过程中,如果把当前状态也传了过来,就一定要开一个备份,直接对备份进行操作,然后把备份作为参数传下去,这之后再把备份改回原来的状态
2.在搜索方案的过程中,如果在往下走之前把judge数组设了一,递归完一定要再改成零
3.在循环搜索填下一个空的时候,如果开了变量,要对状态进行一定处理,并有他进行judge数组记录,在需要回溯的时候一定要看好,这个变量还是不是从一开始穿上来的那个状态引出来的
4.一定要设置边界
5.在判重的时候,在队首元素入队前设标记,在扩展节点前判定标记,在入队新状态前设标记
框架:
1.深搜
void dfs(当前状态); { if 当前状态为边界 { if 当前状态为最佳目标状态 { 记下最优结果;exit;} } for i←算符最小值 to 算符最大值 do {//状态循环变量为局部变量 算符i作用于当前状态,扩展出一个子状态; 标记子状态路径; if (子状态满足约束条件) and (子状态满足最优性要求) dfs(子状态); 恢复子状态路径; //回溯 } }
2.广搜
1 void bfs(v); 2 { 3 Int queue(q); 4 Visite(v); visted[v]=true; Insert_queue(q,w); 5 While not empty(q) do { 6 取出队首元素 v 7 For 对所有v扩展出来的元素w 8 if (not visited[w] ) 9 { visite(w);visited[w]=true; Insert_queue(q,w)} 10 delete_queue(q,v); 11 } 12 }
3.双向广搜
1 //DOUBFS初始化,初始结点,和目标结点分别进入OPEN[1]和OPEN[2]表; 2 head[1]=1; tail[1]=1; head[2]=1; tail[2]=1; 3 do{ 4 if (tail[1]-head[1]<=tail[2]-head[2]) t=1;else t=2;//优先扩展待讨论节点比较少那个方向 5 for(i=head[t];i<=tail[t];i++)expand(t);//讨论队列中的结点 6 }while(head[t]<=tail[t]); 7 //函数expand(t)的结构如下: 8 void expand(int t) 9 { 10 int j; 11 for(j=1;j<=n;j++ ) //n为最多后继状态数 12 { 13 产生i点的第j个后继状态,将它加入到队尾(tail[t]+1); 14 if(新结点未与其上一层以上的所有点重复) 15 { 16 if isans(t) {输出结果;return;} 17 } 18 else{将新点从队列中去掉(tail[t]-1);} 19 } 20 } 21 // 判断两个方向是否是相交点的函数isans(t)如下: 22 bool isans(int t) 23 { 24 int j,k; 25 if(t==1)k=2; else k=1; 26 for(j=head[k] ;j<=tail[k];j++) 27 if(tail[t]==j)return true;//新结点tail[t]是相交点 28 return false; 29 }
4.康托展开式编码判重
X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0!
1 int gethash(status a) 2 { 3 int res = 0; 4 int k = 1; 5 int i, j; 6 //此处的4为每个位能达到的最大数+1 7 for (i = 1; i <= 4; i++) 8 for (j = 1; j <= 4; j++) 9 { 10 res += a.map[i][j] * k; 11 k *= 3; 12 } 13 return res; 14 }
5.位运算加速
·x&-x 返回最低一位1
·x ^ 1 等价于!x