Bootstrap

C++算法——DFS(图解)

前言

这篇原本是两个搜索算法,但是发现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
天坑补完ヽ(°▽、°)ノ好耶!

;