1.dfs基本思想
dfs:深度优先搜索算法(英语:Depth-First-Search,DFS)是一种用于遍历或搜索树或图的算法.沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。属于盲目搜索。简单来说dfs可以看作是一个执着的人,优先往下走。搜索方式如图:
2.算法思想
dfs使用的数据结构是stack 栈,空间复杂度为o(n);dfs中最重要的算法思想是回溯和剪枝。另外dfs不具有最短性。
回溯是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
剪枝,就是减小搜索树规模、尽早排除搜索树中不必要的分支的一种手段。形象地看,就好像剪掉了搜索树的枝条,故称之为“剪枝”。
3.dfs经典例题
由于dfs搜索框架较多,所以我们可以看两个例题来理解dfs如何使用。做题时最重要的是考虑用什么搜索顺序。
3.1AcWing 824 排列数字
给定一个整数 n,将数字 1∼n排成一排,将会有很多种排列方法。现在,请你按照字典序将所有的排列方法输出。
输入格式
共一行,包含一个整数 nn。
输出格式
按字典序输出所有排列方案,每个方案占一行。
数据范围
1≤n≤71≤n≤7
输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
解题思想:首先我们应该先想一下本题的搜索顺序,全排列有很多搜索顺序,这个题搜索顺序:假设我们有n个空位,n=3,我们顺序就是从前往后每一位来填,每一次填的时候和前面不相同就可以了。如图这样,这里需要注意在搜索时不是如下图一样是一个树的形状,而是只有一条路径,每次回溯时会恢复现场,恢复到搜索前的状态。
代码如下:
#include<iostream>
using namespace std;
const int N=10;
int path[N];//存储当前搜索状态
int n;
bool str[N];//判断当前点是否被用过
void dfs(int u)
{
if(u==n)//当我们搜索到最后时,所有位置已经填满了
{
for(int i=0;i<n;i++) printf("%d ",path[i]);//输出结果
puts("");
return;
}
for(int i=1;i<=n;i++)
{
if(!str[i])//如果没有被用过
{
path[u]=i;//记录下当前数
str[i]=true;//表示已经填过
dfs(u+1);//递归到下一个数
str[i]=false;//回溯恢复
}
}
}
int main()
{
cin>>n;//输入n
dfs(0);//从第0个数来搜索
return 0;
}
3.2AcWing 843 n-皇后问题
n−皇后问题是指将 n 个皇后放在 n×nn×n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。
现在给定整数 nn,请你输出所有的满足条件的棋子摆法。
输入格式
共一行,包含整数 n。
输出格式
每个解决方案占 n 行,每行输出一个长度为 n 的字符串,用来表示完整的棋盘状态。
其中 .
表示某一个位置的方格状态为空,Q
表示某一个位置的方格上摆着皇后。
每个方案输出完成后,输出一个空行。
注意:行末不能有多余空格。
输出方案的顺序任意,只要不重复且没有遗漏即可。
数据范围
1≤n≤91≤n≤9
输入样例:
4
输出样例:
.Q..
...Q
Q...
..Q.
..Q.
Q...
...Q
.Q..
解题思路:顺序就是我们可以向全排列那样搜,因为每一行都有放一个皇后所以我们可以枚举每一行的皇后放在什么位置,这里我们需要注意剪枝,我们也可以先写一个全排列,然后在判断每个皇后的位置。
代码如下:
#include<iostream>
using namespace std;
const int N=20;//因为对角线的数量是2n-1,所以我们这里N开两倍
char g[N][N];
int n;
bool col[N],ug[N],aug[N];//判断皇后是否在同一行在同一列,同一对角线
void dfs(int u)
{
if(u==n)//搜索到最后
{
for(int i=0;i<n;i) puts(g[i]);//输出结果
puts(“”);
return;
}
for(int i=0;i<n;i)
{
if(!col[i] && !ug[u+i] &&!aug[n-u+i])//位置合理
{
g[u][i]=’Q’;//让当前位置为Q
col[i]=ug[u+i]=aug[n-u+i]=true;//表示已经使用过此位置
dfs(u+1);//递归到下一个数
g[u][i]=’.’;//恢复原来状态
col[i]=ug[u+i]=aug[n-u+i]=false;//恢复原来状态
}
int main()
{
cin>>n;
for(int i=0;i<n;i)
for(int j=0;j<n;j)
g[i][j]=’.’;
dfs(0);//从0开始搜索
return 0;
}