DFS
全称为Depth First Search,中文称为深度优先搜索。
这是一种用于遍历或搜索树或图的算法,其思想是:
沿着每一条可能的路径一个节点一个节点地往下搜索,
直到路径的终点,然后再回溯,直到所有路径搜索完为止。
DFS俗称爆搜,最重要的就是顺序
需要思考的是,应该用什么样的顺序来搜索答案
用树的形式展开
假设已经有n个空位了,那么就从第一个开始填,
从前向后一个空位一个空位的填充,且每一次填充的数字不能与前面填充的数字一样
深搜是优先向下走,即先填充好一条路径,填好后,会向前回溯。
每次存的都是当前的路径,回溯后前面的路径就不存在了,
所以整体看来是一棵树,实际上每次只能有一条分支,且最后回溯之后就什么也没了
题目:842. 排列数字 - AcWing题库
思路:
思路挺简单的:一层for循环,如果当前的数没有被用过,那么就使用它。
st[i]表示当前的数是否被用过
path[x]用来储存将要被使用的数
这里dfs中,dfs的路径表示的是长度,即不断向下深搜
第二个for循环表示的是宽度,即向右深搜,直到找到一个没有被用过的数字。
tips
可以自己模拟一下,但是要注意该层dfs()结束的标志可以是第一层for循环结束,也可以是第二层for循环结束,而且在回溯的过程中,只要存在满足第二层循环里的if(!st[i])语句就会开辟新的路径,向下深搜
代码:
#include<iostream>using namespace std;int path[10];
bool st[10];
int n;void dfs(int x)
{//说明到最后一排——即走完了该条路径if(x==n){for(int i=0;i<n;i++) cout << path[i] << " ";cout << endl;}else {for(int i=1;i<=n;i++){//false表示没有被用过if(!st[i]){path[x]=i;//使用ist[i]=true;//i被使用过了dfs(x+1);//清空上一路径的数值以及操作path[x]=0;st[i]=false;}}}
}
int main()
{cin >> n;dfs(0);return 0;
}
DFS+剪枝
剪枝,顾名思义就是减去某些枝条且是没用的枝条
返回到DFS中 就是在向下深搜的过程中,如果碰到了与题目要求不符的条件,那就停止向下深搜,转为向前回溯。即可以提前判断当前操作一定是不合法的,那么就没必要继续向下搜了,直接结束,向前回溯。该操作称之为剪枝。
题目:843. n-皇后问题 - AcWing题库
思路:
题目要求:如果该点已经有皇后了,那么该点所在的行、列,正对角线、反对角线上都不能再有第二个皇后
边深搜便判断是否与题解相斥:如果与题目要求相斥,那么就停止向下搜索,转为向前回溯
不想手动模拟的话,那么可以看一看这一篇题解:AcWing 843. n-皇后问题--图解+代码注释 - AcWing
代码:
#include<iostream>using namespace std;const int N=20;
//记录棋盘布局
char cow[N][N];
//l[N]表示这一列是否含有女皇,dg[N]表示该女皇的正对角线是否含有女皇,udg[N]反对角线
int l[N],dg[N],udg[N];
int n;
//深度搜索每一行
void dfs(int u)
{if(u==n){for(int i=0;i<n;i++){for(int j=0;j<n;j++)printf("%c",cow[i][j]);printf("\n");}printf("\n");}for(int i=0;i<n;i++){//如果满足该女皇所在列、对角线、反对角线都没有别的女皇的话if(!l[i] && !dg[u+i] && !udg[n-i+u]){//则在u行i列放下一个女皇cow[u][i]='Q';//则新放下女皇所在的列、正对角线、反对角线都不能再放置其他女皇了l[i]=dg[u+i]=udg[n-i+u]=true;//搜索下一行是否能放置女皇dfs(u+1);//还原现场l[i]=dg[u+i]=udg[n-i+u]=false;cow[u][i]='.';}}return;
}int main()
{scanf("%d",&n);//初始化for(int i=0;i<n;i++){for(int j=0;j<n;j++)cow[i][j]='.';}dfs(0);return 0;
}
tips:
dfs强烈建议手动模拟一下,要注意:dfs结束的方式不只是return,还有for循环的结束也会造成dfs的结束。这层dfs结束后就会执行上一个dfs剩下的“还原现场”的语句,接着根据是否结束本层for循环来决定是否结束上一个dfs语句。