所谓深搜(也叫回溯法)就是采用的是“一直往下走,走不通了就掉头,换一条路再往下走”
总结来说就是递归的枚举
深度优先搜索的实质就是穷举,按照一定的顺序和规则不断地去尝试,直到找到问题的解。
对于一个问题的第一个状态叫做初始状态,最后要求的状态叫做目的状态。
在搜索的过程中,对当前状态进行检测,如果当前状态满足目的状态,那么这个当前状态就是结果之一。
为什么要取消标记:
深搜搜到底以后,结束dfs(),该点不会继续被他的父亲节点再次搜到,即便已经取消标记,因为有一个for循环,下一次会再次访问与它处于同一级的其他节点
模板:
void dfs(int x,int y){
if(vis[x][y])
return ;
for(int i=0;i<4;i++){//向各个方向
int fx=x+mov[i][0];
int fy=y+mov[i][1];
if(fx<0||fy<0||fx>=tmp1||fy>=tmp2)//不要越界
continue;
vis[fx][fy]=1;//标记
dfs(fx,fy);
vis[fx][fy]=0;//取消标记
}
}
深搜的特点:
(1)深度优先搜索法有递归以及非递归两种设计方法。一般的,当搜索深度较小、问题递归方式比较明显时,用递归方法设计好,它可以使得程序结构更简捷易懂。当数据量较大时,由于系统堆栈容量的限制,递归容易产生溢出,用非递归方法设计比较好。
(2)深度优先搜索方法有广义和狭义两种理解。广义的理解是,只要最新产生的结点(即深度最大的结点)先进行扩展的方法,就称为深度优先搜索方法。在这种理解情况下,深度优先搜索算法有全部保留和不全部保留产生的结点的两种情况。而狭义的理解是,仅仅只保留全部产生结点的算法。本书取前一种广义的理解。不保留全部结点的算法属于一般的回溯算法范畴。保留全部结点的算法,实际上是在数据库中产生一个结点之间的搜索树,因此也属于图搜索算法的范畴。
(3)不保留全部结点的深度优先搜索法,由于把扩展望的结点从数据库中弹出删除,这样,一般在数据库中存储的结点数就是深度值,因此它占用的空间较少,所以,当搜索树的结点较多,用其他方法易产生内存溢出时,深度优先搜索不失为一种有效的算法。
(4)不一定会得到最优解,这个时候需要修改原算法:把原输出过程的地方改为记录过程,即记录达到当前目标的路径和相应的路程值,并与前面已记录的值进行比较,保留其中最优的,等全部搜索完成后,才把保留的最优解输出。
优化的方式(剪枝):
1.优化搜索顺序:产生不同的搜索树形态,规模就会有所不同
2.排除等效冗余:排除产生相等结果的可能
3.可行性剪枝:排除那些已经无论如何都无法达到递归边界的分支
4.最优性剪枝:当前花费已经超过了当前得到的最优解
5.记忆化
其他的拓展:
1.迭代加深:用在可以确保答案在一个比较浅层的节点(每次搜索的时候,限定层级)
2.双向搜索(待补充)
例题:
1.蜘蛛牌
2.符号三角形
3.Oil Deposits(判断联通体)
4.Rescue (解救小伙伴)
5.Prime Ring Problem(深搜+素数判断)
6.小猫爬山