文章目录
一、引入
搜索分为两大类——深度优先搜索算法和广度优先搜索算法。这里我给大家介绍一下深度优先搜索算法(简称深搜)。它通过递归(即函数调用自身)的方式,一步步向答案逼近,最终得出正确答案。但值得注意的是,这种算法未经剪枝优化会特别慢,很容易超时。深搜被广泛应用到多个领域。在比赛中,深搜的思路也是特别好想的,在想不出别的思路时可以用深搜骗点分。剪枝什么的暂时先不讲,下面我们先介绍一下栈,再简介一下深搜模板,最后通过几道例题讲解一下。
二、栈
栈(stack)是一种常见的数据结构,一般我们可以用数组实现。我们把栈里面的每一个元素就是一块板子。我们要把板子堆起来,可以想象,我们只能把最新那块板子放在之前最上面那块板子之上。同理,我们一次只能取走最上面的那块板子。假如我们把所有板子放完后再逐个拿出,不难发现最后一个放上去的板子第一个被拿出;第一个被放上去的板子最后一个拿出。因此,栈也有“先进后出表”之称。我们用一个数组stk[]表示栈,并用一个变量top记录栈顶(即最上面的元素)的位置。现在要加入一个元素x,我们只要将top加一,在给栈顶赋值x即可,也就是说stk[++top]=x
。弹出栈顶元素只需要将top减一就行了,即top-=1
。
三、深度优先搜索算法
下面简介一下深搜的主要思想。不难发现,对于每一个决策点,到达终点的方案数唯一。在当前遍历到的决策点,我们一边枚举下一步走法,一边直接走下一步,到达一个里终点更近的子决策点。在新的决策点,我们采取同样的方式,从而一步步向终点靠近,最终到达终点或终点附近,直接返回一个确定的值。那么每一个决策点的可行方案应该为所有子决策点的可行方案(加上当前走法)。
下面展示一段深搜的伪代码:
int dfs(int x){
if(x为终点){
统计(输出);
return;
}
for(y为x的子决策点){
dfs(y);
统计答案;
}
return;
}
若果决策点没有重复,我们发现所有决策点构成一棵树,否则构成一张有向无环图。对于后面的情况,我们一般采用记忆化搜索的方式进行优化,也就是说,在到达一个新决策点时,我们用一个数组记录它,当再次到达同一个决策点时,只需用O(1)的复杂度返回就行了。
事实上每当到枚举每一个子决策点时,就相当于把新的决策点放入栈顶。当访问完它所引出的所有决策点后便能把他从栈顶取出,否则继续找出它所引出的决策点加入栈顶。在它所有引出的决策点求解完之前,它始终不在栈顶,便无法取出即无法求解。一开始栈中只有一个元素即起点,当求完所有决策点时,我们便发现栈空了。也就是说,每一次深搜等价于一个栈。
四、经典例题
下面我们通过具体问题更深入地了解深搜
1.自然数的拆分问题
任何一个大于1的自然数n,总可以拆分成若干个小于n的正整数之和。现在给你一个自然数n,要求你求出n的拆分