算法:dfs(深度优先搜索算法)

文章介绍了深度优先搜索(DFS)的概念,将其比喻为词典查找,强调了递归的性质。文中提到,通过剪枝优化暴力搜索,避免无用的路径。接着,以全排列和n-皇后问题为例,展示了如何应用DFS解决实际问题,并给出了两种不同的DFS实现策略。
摘要由CSDN通过智能技术生成

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值