dfs:暴搜,一条路走到黑,然后回溯,开启另一条路,再一条路走到黑
那么如何对暴搜进行优化?剪枝:当发现某一条树枝时不可能找到答案时,就没必要还在这条路走到黑一直搜索了,将这条树枝减去,趁早换一条路
如图:
可以用递归来一条路走到黑
递归最恰当的比喻,就是查词典。我们使用的词典,本身就是递归,为了解释一个词,需要使用更多的词。当你查一个词,发现这个词的解释中某个词仍然不懂,于是你开始查这第二个词,可惜,第二个词里仍然有不懂的词,于是查第三个词,这样查下去,直到有一个词的解释是你完全能看懂的,那么递归走到了尽头,然后你开始后退,逐个明白之前查过的每一个词,最终,你明白了最开始那个词的意思。这意味着递归是一条路走到黑,递归函数自己调用自己,当递归函数里面的递归函数执行完成了,该递归函数才能执行完成,而里面的递归函数里面还有递归函数,里面的递归函数里的递归函数里还有递归函数,也就是一直有嵌套,里面的执行了才能执行外面的,这样一条路走到黑,设置递归结束的条件,当满足条件则说明走到黑了,然后去能执行完成它外面的递归函数,一直到执行完最开始的递归函数
如:想完成2则需要完成2.4.8.11和2.5.6,当完成2.4.8.11之后时一条路已经走到黑了则需要回溯到2走另一条路接着递归2.5.9这条路走到黑
例1:数字的全排列
给定一个整数 n,将数字 1∼n排成一排,将会有很多种排列方法。现在,请你按照字典序将所有的排列方法输出。
输入格式
共一行,包含一个整数 n。
输出格式
按字典序输出所有排列方案,每个方案占一行。
数据范围
1≤n≤7
输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
#include<iostream>
using namespace std;
const int N=10;
int n;
int path[N];
bool st[N];
void dfs(int u){
if(u==n){
for(int i=0;i<n;i++) cout<<path[i];
cout<<endl;
return;
}
for(int i=1;i<=n;i++){
if(!st[i]){
path[u]=i;
st[i]=true;
dfs(u+1);
st[i]=false;
}
}
}
int main()
{
cin>>n;
dfs(0);
return 0;
}
手动模拟递归过程,如图:
dfs(u+1)进行递归一条路走到黑,直至搜索到三个不同的数,并输出,然后st[i]=false进行回溯,恢复现场,恢复到搜索前的状态,再去搜索另一条路
dfs(0)表示从最初的状态开始搜索
递归,首先要有递归结束的条件,否则递归一直嵌套,就永远出不来了,其次第一步怎么做要写出来,然后才能不断递归
例2:n-皇后问题
n−皇后问题是指将 n 个皇后放在 n×n的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。
现在给定整数 n,请你输出所有的满足条件的棋子摆法。
输入格式
共一行,包含整数 n。
输出格式
每个解决方案占 n 行,每行输出一个长度为 n 的字符串,用来表示完整的棋盘状态。
其中 . 表示某一个位置的方格状态为空,Q 表示某一个位置的方格上摆着皇后。
每个方案输出完成后,输出一个空行。
注意:行末不能有多余空格。
输出方案的顺序任意,只要不重复且没有遗漏即可。
数据范围
1≤n≤9
法一:像全排列一样暴搜,u代表行,i代表列,每一行找到一个可以放置的位置i,放上皇后
#include<iostream>
using namespace std;
const int N=20;
int n;
char g[N][N];
bool col[N],dg[N],udg[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]&&!dg[i+u]&&!udg[i-u+n]){
g[u][i]='Q';
col[i]=dg[i+u]=udg[i-u+n]=true;
dfs(u+1);
g[u][i]='.';
col[i]=dg[i+u]=udg[i-u+n]=false;
}
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
g[i][j]='.';
}
}
dfs(0);
return 0;
}
法二:一个格子一个格子进行枚举,每个格子考虑两种可能,并都进行试验,放皇后和不放皇后
挨个枚举每个格子,每个格子都是放和不放,放是一种分支,不放是另一种分支
x从0开始,当x=n时递归就可以结束了
#include<iostream>
using namespace std;
const int N=20;
int n;
char g[N][N];
bool row[N],col[N],dg[N],udg[N];
void dfs(int x,int y,int s){
if(y==n) y=0,x++;
if(x==n){
if(s==n){
for(int i=0;i<n;i++) puts(g[i]);
puts("");
}
return;
}
dfs(x,y+1,s);
if(!row[x]&&!col[y]&&!dg[x+y]&&!udg[x-y+n]){
g[x][y]='Q';
row[x]=col[y]=dg[x+y]=udg[x-y+n]=true;
dfs(x,y+1,s+1);
g[x][y]='.';
row[x]=col[y]=dg[x+y]=udg[x-y+n]=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,0);
return 0;
}