前言
这篇原本是两个搜索算法,但是发现BFS那个单独看的人多,所以这篇改为单独的DFS,建议先看完BFS
简介
深度优先搜索算法是最简便的图的搜索算法之一。其别名又叫DFS,其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次,但在类似迷宫的问题中效率较低。
迷宫
还是以好理解的迷宫举例子
0是路,1是墙,2是入口,3是出口
0 0 1 0 1
2 1 0 0 3
0 0 0 1 0
0 1 0 1 0
0 1 0 0 0
也就是
把走过的路标记为2
那么来看下DFS是怎么走的
开始上图
从起点一个方向走到头
死路,那么后退直到有别的方向
继续
那么你可能就会问了,这不对啊,最短路明明是走上面啊
别急,这就是它走迷宫效率不高的原因,他有很多种走到终点的方法,但是不一定是最快的,这有时候取决于你的方向设定,也许某个顺序你就能蒙到最短路,但往往没这可能,所以我们可以让他把所有路走一遍,比较出那个最短的
DFS的精髓就在于return,利用好递归以及条件的判定
代码
上面问题的代码(到了就行,不一定最短)
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
int m,n,sum=0,ok=1; //看下面
int dir[4][2] = {{1,0},{0,1},{-1,0},{0,-1}}; //看我!看我!看我!方向可以自己改改看有什么效果
int Map[5][5]; //看过了,回上面
int l,t=0;
vector<pair<int,int> > q;
bool judge(int x1,int y1){//判边界
return (x1>=0&&x1<=4&&y1>=0&&y1<=4&&(Map[x1][y1]==0||Map[x1][y1]==3));
}
void dfs(int x,int y){
if(ok)//到了就不跑了,后面全过
return;
for(int i=0;i<4;i++){
int x1 = x+dir[i][0];
int y1 = y+dir[i][1];
if(judge(x1,y1)){
if(Map[x1][y1]==3){ //到终点
ok=1;
cout << sum<<endl;
return;
}
Map[x1][y1]=2;
for(int i=0; i<5; i++){ //打印地图
for(int j=0; j<5; j++)
cout << Map[i][j];
cout << endl;
}
cout << endl;
pair<int,int> z(x1,y1);
q.push_back(z);
sum++;
dfs(x1,y1);
if(ok)
return;
Map[x1][y1]=0;
for(int i=0; i<5; i++){ //打印地图
for(int j=0; j<5; j++)
cout << Map[i][j];
cout << endl;
}
cout << endl;
q.pop_back();
sum--;
}
}
}
int main()
{
int sx,sy;
ok = 0;
for(int i=0;i<=4;i++){
for(int j=0;j<=4;j++){
cin>>Map[i][j];
if(Map[i][j]==2){
sx = i;
sy = j;
}
}
}
pair<int,int> z(sx,sy);
q.push_back(z);
dfs(sx,sy);
return 0;
}
例题
1.来个迷宫练练手
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
int m,n;
int vis[5][5];
int f[4][2] = {{0,-1},{-1,0},{0,1},{1,0}};
int a[5][5];
int l,t=0;
vector<pair<int,int> > q;
vector<pair<int,int> > q2;
bool judge(int x1,int y1){
return (x1>=0&&x1<=4&&y1>=0&&y1<=4&&vis[x1][y1]==0&&a[x1][y1]==0);
}
void dfs(int x,int y){
if(x == 4&&y== 4){
if(t==0|| q2.size()>q.size()){
t = 1;
l = q.size();
q2.clear();
for(int i=0;i<q.size();i++){
q2.push_back(q[i]);
}
}
return ;
}
for(int i=0;i<4;i++){
int x1 = x+f[i][0];
int y1 = y+f[i][1];
if(judge(x1,y1)){
vis[x1][y1]=1;
pair<int,int> z(x1,y1);
q.push_back(z);
dfs(x1,y1);
vis[x1][y1]=0;
q.pop_back();
}
}
}
int main()
{
memset(a,0,sizeof a);
memset(vis,0,sizeof vis);
for(int i=0;i<=4;i++){
for(int j=0;j<=4;j++){
cin>>a[i][j];
}
}
vis[0][0]=1;
pair<int,int> z(0,0);
q.push_back(z);
dfs(0,0);
for(int i=0;i<l;i++){
cout << "(" << q2[i].first << ", " << q2[i].second << ")";
if(i!=l-1)
cout << endl;
}
return 0;
}
那么问题又来了,既然这样我为什么要用麻烦的DFS?
这是因为BFS由于同步走,也就意味着同步存储,当数据量过大时,占用的空间将是很大的量,一些题是会超内存的。(聪明反被聪明误?)同样道理,DFS相当于遍历,因此总是会超时间。(太耿直了也不行啊)
2.遍历
依旧简单的一题!
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
int m,n,p;
int a[10][10];
int b[10][10];
int vis[10][10];
int f[8][2] = {{-2,-1},{-2,1},{-1,2},{1,2},2,1,2,-1,1,-2,-1,-2};
vector<pair<int,int> > q;
int xb,yb,xe,ye,sum=0;
bool judge(int x1,int y1){
return (x1>=0&&x1<m&&y1>=0&&y1<n&&vis[x1][y1]==0);
}
void dfs(int x,int y){
// cout<<"x1: "<<x<<" y1:"<<y<<endl;
p=1;
for(int i=0;i<m;i++)
{
for(int l=0;l<n;l++)
{
if(vis[i][l]==0)
{
p=0;
break;
break;
}
}
}
if(p){
sum++;
return ;
}
for(int i=0;i<8;i++){
int x1 = x+f[i][0];
int y1 = y+f[i][1];
if(judge(x1,y1)){
vis[x1][y1]=1;
pair<int,int> z(x1,y1);
q.push_back(z);
dfs(x1,y1);
vis[x1][y1]=0;
q.pop_back();
}
}
}
int main()
{
int o;
cin >> o;
while(o--){
sum = 0;
memset(vis,0,sizeof (vis));
cin>>m>>n;
cin>>xb>>yb;
vis[xb][yb]=1;
pair<int,int> z(xb,yb);
q.push_back(z);
dfs(xb,yb);
cout << sum << endl;
}
return 0;
}
3.一个找不到的题目
貌似是计蒜客的,大概复述一下,你可以自己造点数据
给你一串字符,输出变化后的样子,给几个例子(不保证完全和原题一样,只能说差不多)
3(h)->hhh
2(2(a)b)->2(aab)->aabaab
b1(2(an)a)->banana
你可以自己搞更多重,我偷个懒 ̄ω ̄=
4.救救小狗
之前说过这个玩意时间复杂度会很高,那么一个很必要的技巧就是剪枝,也就是提前结束无用过程,祝你时间达标hhh
题目链接
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
int m,n;
int vis[100][100];
int f[4][2] = {{0,-1},{-1,0},{0,1},{1,0}};
string b,a[100];
int t,flag;
int xs,ys,xl,yl;
vector<pair<int,int> > q;
bool judge(int x1,int y1){
return (x1>=0&&x1<m&&y1>=0&&y1<n&&vis[x1][y1]==0&&a[x1][y1]!='X');
}
void dfs(int x,int y,int l){
if(flag)
return;
if(x == xl&&y== yl){
if(l==t){
flag=1;
}
return ;
}
if(abs(x-xl)+abs(y-yl)+l>t || (t-l-abs(x-xl)-abs(y-yl))%2)
return;
for(int i=0;i<4;i++){
int x1 = x+f[i][0];
int y1 = y+f[i][1];
if(judge(x1,y1)){
vis[x1][y1]=1;
dfs(x1,y1,l+1);
vis[x1][y1]=0;
}
}
}
int main()
{
while(cin >> m >> n >> t)
{
if(m==0)
return 0;
flag = 0;
for(int i=0;i<m;i++)
{
cin >> a[i];
for(int j=0;j<n;j++)
{
if(a[i][j]=='S'){
xs=i;
ys=j;
}else if(a[i][j]=='D'){
xl=i;
yl=j;
}
}
}
memset(vis,0,sizeof vis);
vis[xs][ys]=1;
dfs(xs,ys,0);
if(flag)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
return 0;
}
5.别的应用
除了地图,我们还可以应用到别的方面
题目链接
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int n,sum,l,flag=0;
int a[65];
int vis[65];
void dfs(int m,int l2){ //个数,缺少长度
if(flag)
return;
if(m==0 && l2==0)
{
flag=1;
}
if(flag)
return;
if(l2==0)
l2 = l;
for(int i=0;i<n;i++)
{
if(!vis[i] && a[i]<=l2)
{
if(i>0)
{
if(!vis[i-1]&&a[i]==a[i-1])
continue;
}
vis[i]=1;
dfs(m-1,l2-a[i]);
vis[i]=0;
if(a[i]==l2||l2==l)
return;
}
}
return;
}
bool cmp(int a,int b)
{
return a>b;
}
int main()
{
while(cin >> n && n!=0)
{
flag=0;
sum=0;
for(int i=0;i<n;++i)
{
cin >> a[i];
sum+=a[i];
}
sort(a,a+n,cmp);
for(l=a[0];l<=sum/2;++l)
{
if(sum%l)
continue;
memset(vis,0,sizeof(vis));
dfs(n,l);
if(flag)
{
cout << l << endl;
break;
}
}
if(l>sum/2)
cout << sum << endl;
}
return 0;
}
dfs就是刚,不撞三个墙不回头!
这里仅仅从算法的角度去观察,所以不要忘了别的基础理论a
天坑补完ヽ(°▽、°)ノ好耶!