当前位置: 首页 > >

深度优先搜索及其优化

发布时间:

深度优先搜索(DFS)是最常用的搜索算法之一,俗称一条路走到黑。


在一般的搜索题中如果不是卡数据点的题目一般都可以直接用深搜来做,深搜也可以用某些题目的骗分【挑眉


在进行搜索的时候,选择一个没有被搜过的结点,按照深度优先,一直往该结点的后续路径结点进行访问,直到该路径的最后一个结点,然后再从未被访问的邻结点进行深度优先搜索,重复以上过程,直至所有点都被访问,搜索结束。【搬百度qwq


深搜对于解决迷宫、n皇后问题很舒服,比如给出下面一个迷宫


001


011


000


其中1代表*铮0代表可通行的路。对于这个迷宫图来说,我们要从左上角走到右下角用深搜的话,若先由(1,1)向右走,走到(1,2)后往右(1,3)是*铮豢尚校伦撸2,2)也是*铮戏矫挥新罚跃鸵厮莸剑1,1),此时,(1,2)方向的路已经走完了,从(1,1)就只剩了往下的一条路。这样说是不是很好理解?


那么我们来看一下最经典的八皇后的问题。八皇后是一个很老的题了,貌似是19世纪提出的,对于八皇后问题用数学来解答那就很爽了【滑稽】不过我们一个深搜全搞定( ?? ω ?? )


https://www.luogu.org/problemnew/show/P1219


这是洛谷上的n皇后问题,不过这n不能太大,不然你懂的。


题目中说每一个皇后攻击范围是她所在的行、列和对角线。


偷来洛谷的图( ̄? ̄)


我们来看这样一个规律,其余的皇后我们不看,现在假设只有(5,3)这个点上的皇后


那么意味着整个第五行和第三列以及她的对角线都在她的攻击范围内。行和列很好表示,那么对角线怎么弄呢?


这时候我们仔细观察,(5,3)这个皇后的左上到右下对角线有(3,1),(4,2),(5,3),(6,4)这时我们可以看到,在这几个坐标中,前面-后面都等于一个常数。同理左下到右上这一条对角线的坐标,前面+后面都等于一个常数。


因此我们就可以表示对角线c[i+j+100]和d[i-j+100]这里的100是随便写的,只要保证i-j+q(q为常数)>0就可以,因为这样数组才不会出现负值下标。


在深搜算法的过程中,注意要规定边界,如果没有边界,那就是一个死循环了,分分钟炸掉emmm


下面贴上n皇后的代码;


#include
using namespace std;
int n,b[15],c[226],d[226],sign[16];//定义b[]代表列,c[],d[]代表对角线,sign[]标记此行是否访问完。
int sum;//总方案数
void dfs(int cur)
{
if(cur==n+1)//当访问过的行数大于总行数,说明已经摆好一种方案,sum++,返回
{sum++;return;}
for(int i=1;i<=n;i++)
{
if(b[i]==0&&c[i-cur+100]==0&&d[i+cur+100]==0)
{
b[i]=1;
c[i-cur+100]=1;
d[i+cur+100]=1;
sign[cur]=1;
dfs(cur+1);
b[i]=0;
c[i-cur+100]=0;
d[i+cur+100]=0;
//注意回溯之后在下面要清0,否则无法进行下一波摆放
}
}
return ;
}
int main()
{
cin>>n;
dfs(1);
cout< return 0;
}

对于测试数据都可以通过,不过千万要注意在回溯之后要清零处理,不然有bug的。


深搜固然好,但是对于一些题目来说深搜也显得特别弱了。


不过深搜也可以优化,比如剪枝优化,所谓剪枝就是在程序运行之前通过某种特定关系进行判断,把不必要的搜索剪掉。也就是说如果我们要得到一个最优解,在进行深搜的过程中到了这一步,此时你已经可以确定此路往下没有更优解,那么直接减掉return即可。这样就可以减少运算次数,有效地优化搜索算法。


在洛谷水题的时候遇到一个k皇后的题,题目不算难但是还是没忍住用了深搜,emm虽然现实很残酷只得了20分,数据用深搜太黑暗了qwq,不过用深搜的话可以剪一下枝hh也挺典型的,所以还是把这个20分代码交上来emm


题目的大体描述是:给出一个n*m的矩阵,放置k个皇后,问不在皇后攻击范围内的点有几个。


我当时一股脑热的第一思路emm深搜深搜深搜!!


因为放完一个皇后之后她攻击范围内的就不可能有存活地点了,所以在深搜行的时候可以进行一个特判,也就是说如果这一行有皇后,直接过掉。奈何数据不给面子qwq,下面代码可看可不看


#include
using namespace std;
int k,o,p,sum=0;
int a[10001],b[10001],c[10001],d[10001];
void dfs(int i)
{
//cout<<"第"< if(a[i]==1)
{
dfs(i+1);
return ;
}
if(i==o+1)
return;
for(int j=1;j<=p;j++)//i==3;j==3;
{
if(b[j]!=1&&c[i+j+100]!=1&&d[i-j+100]!=1)
sum++;
}
//cout<<"qwq"< dfs(i+1);
}
int main()
{
cin>>o>>p;
cin>>k;
for(int i=1;i<=o;i++)
for(int j=1;j<=p;j++)
a[i]=0,b[j]=0;
for(int i=1;i<=k;i++)
{
int m,n;
cin>>m>>n;
a[m]=1;
b[n]=1;
c[m+n+100]=1;
d[m-n+100]=1;
}
dfs(1);
//cout<<"qwqqq";
cout< return 0;
}

剪枝也没什么特别固定的标准,就是把已知可以判断出来无用的部分给剪掉不去执行,以减少运行次数和算法复杂度。


复杂的深搜题一般都有枝可以剪掉优化,其中还可以根据搜索的顺序进行剪枝优化,或者最优化剪枝【大家应该都晓得,就不多说了】一个题目这样多次优化应该也就没有多大的*恕


个人感觉,剪枝很重要x1,嗯,很重要x2很重要x3;


??深搜初步学*( ̄? ̄)各位大佬请无视,错误请尽管指出来谢谢(*/ω\*)


?


?



友情链接: