算法07 深度优先搜索及相关问题详解

深搜与广搜是搜索算法中最常用的两种算法,通过深度优先搜索解决问题还会用到回溯和剪枝,让我们一起进入本章,了解深搜的基本概念和模板,并学会解决一些常见问题。

目录

问题导入

走迷宫问题

如何走?

问题建模

如何表示迷宫地图等信息呢?

如何表示每次移动的过程?

走迷宫问题参考程序

深度优先搜索概述

解决问题时的注意事项

训练:迷宫

参考代码

训练:全排列问题

参考代码


问题导入

走迷宫问题

现在有一个3*3的迷宫,小知在迷宫的左上角,迷宫出口在右下角,请你帮小知算一算,他有多少种方案可以走出迷宫(每个格子不能重复走动)。

迷宫中显示0的点,是不可以走的。小知每次只能到达相邻的上下左右4个格子。

如何走?

1.如果当前出发的格子是终点,则程序结束。

2.每次可以选择的格子有:上(A)、下(B)、左(C)、右(D)。

3.按照顺序尝试每个格子是否可走。

4.找到第一个可以走的格子(假设为B),标记该格子,表示已经走过。

5.再从格子(B)出发,重复上述过程(递归进行)。

6.将格子(B)消除标记,再尝试从格子(C、D)出发去找路线。

问题建模

如何表示迷宫地图等信息呢?

使用一个3*3的二维数组maze\[\]\[\]来存储迷宫信息,如果值位0表示不可走,1表示可走。

使用一个3*3的二维数组used\[\]\[\]来标记是否走过,没走过为0,走过的话为1。

例:used12=1,表示maze12已经走过。

如何表示每次移动的过程?

每次移动,实际上就是坐标的变化。

上:行标-1,列标不变。

下:行标+1,列标不变。

左:行标不变,列标-1。

右:行标不变,列标+1。

我们可以用一个二维数组表示移动的方向。

例:当前坐标为(1,2),向上移动就是:(1+fx00,2+fx01),得到(0,2)。

走迷宫问题参考程序

复制代码
int ans=0,maze[6][6],used[6][6],fx[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
void dfs(int x,int y){
    if(x==3&&y==3){ //如果当前找的点就是终点,则方案数+1,结束本次搜索。
        ans++; return ;
    }
    else{
        used[x][y]=1;  //将当前所在的点标记为1,表示走过
        for(int i=0;i<4;i++) { //尝试上、下、左、右4个方向
            int nx=x+fx[i][0];int ny=y+fx[i][1]; //下一次查找的点的坐标nx,ny
            //nx,ny要在地图内,并且可走,并且未被走过。
            if(nx>=1&&nx<=3&&ny>=1&&ny<=3&&used[nx][ny]==0&&maze[nx][ny]==1){
                used[nx][ny]=1;  //表示nx,ny表示走过
                dfs(nx,ny); //从nx,ny再去找
                used[nx][ny]=0; //消除标记,再尝试其他方向。
            }
        }
        used[x][y]=0; //消除标记
    }
}

int main(){
    for(int i=1;i<=3;i++){
        for(int j=1;j<=3;j++){
            cin>>maze[i][j];
        }
    }
    dfs(1,1);
    cout<<ans;
    return 0;
}

深度优先搜索概述

我们走迷宫的过程就是一个深度优先搜索的过程:从可以解决问题的某一个方向出发,并一直深入寻找,找到这个方向可以得到的所有解决方案,如果找不到,则回退到上一步,从另一个方向开始,再次深入寻找。

解决问题时的注意事项

1.首先弄清楚问题的解空间,即迷宫有多大。

2.弄清楚搜索的边界,即到哪一步就该停下,不用再搜。

3.搜索的方向,即可能包含哪几种子问题。

复制代码
void dfs()//参数用来表示状态  
{  
    if(到达终点状态){  
        ...//根据题意添加  
        return;  
    }  
    if(越界或者是不合法状态)  
        return;  
    if(特殊状态)//剪枝
        return ;
    for(所有可能的下一状态){  
        if(状态符合条件){  
            修改操作;//根据题意来添加  
            标记;  
            dfs();  
            (还原标记);  
            //是否还原标记根据题意  
            //如果加上(还原标记)就是 回溯法  
        }  
    }  
}

训练:迷宫

给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过。给定起点坐标和终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案。在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。

【输入描述】第一行N、M和T,N为行,M为列,T为障碍总数。第二行起点坐标SX,SY,终点坐标FX,FY。接下来T行,每行为障碍点的坐标。

【输出描述】给定起点坐标和终点坐标,问每个方格最多经过1次,从起点坐标到终点坐标的方案总数。

【输入样例】2 2 1

1 1 2 2

1 2

【输出样例】1

参考代码

复制代码
#include<bits/stdc++.h>
using namespace std;

int ans=0,maze[6][6],used[6][6];
int Fx[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
int n,m,t,sx,sy,fx,fy,tx,ty;

void dfs(int x,int y,int s,int e){
    if(x==s&&y==e){
        ans++; return ;
    }
    else{
        used[x][y]=1;
        for(int i=0;i<4;i++) {
            int nx=x+Fx[i][0];int ny=y+Fx[i][1];
            if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&used[nx][ny]==0&&maze[nx][ny]==0){ 
                used[nx][ny]=1;
                dfs(nx,ny,s,e);
                used[nx][ny]=0;
            }
        }
        used[x][y]=0;
    }
}

int main(){
    cin>>n>>m>>t;
    cin>>sx>>sy>>fx>>fy;
    for(int i=1;i<=t;i++){
        cin>>tx>>ty;
        maze[tx][ty]=1;
    }
    dfs(sx,sy,fx,fy);
    cout<<ans;
    return 0;
}

训练:全排列问题

输入整数n(n<=9),输出1~n的所有排列方式。

例:n=3,全排列为123,132,213,231,312,321。

【输入描述】输入一个正整数n

【输出描述】输出1~n之间的全排列(n<=9),换行输出

【输入样例】3

【输出样例】123

132

213

231

321

312

参考代码

复制代码
int ans[10],used[10];
void dfs(int x,int n){
    if(x>n){
        for(int i=1;i<=n;i++)
            cout<<ans[i];
        cout<<endl;
        return;
    }
    for(int i=1;i<=n;i++){
        if(!used[i]){
            ans[x]=i;
            used[i]=1;
            dfs(x+1,n);
            used[i]=0;
        }
    }
}
int main(){
    int n;
    cin>>n;
    dfs(1,n);
    return 0;
}

从入门到算法,再到数据结构,查看全部文章请点击此处http://www.bigbigli.com/

相关推荐
初学者,亦行者4 分钟前
算法设计与分析3:贪心法 - 求解最短路径问题(TSP)
算法·代理模式
Starmoon_dhw9 分钟前
题解:P16108 「o.OI R-1」基础博弈练习题
c++·算法·图论
大鱼>21 分钟前
AI+快递分拣:视觉识别+自动分拣+异常检测
人工智能·深度学习·算法·机器学习
想要成为糕糕手23 分钟前
238. 除了自身以外数组的乘积 — 面试向深度解析
javascript·算法·面试
2601_9498163530 分钟前
C++内存对齐与结构体填充深度精讲:底层规则、内存裁剪、适配优化与工程避坑
开发语言·arm开发·c++
浩瀚地学35 分钟前
【面试算法笔记】0105-数组-螺旋矩阵
java·开发语言·笔记·算法·面试
王老师青少年编程1 小时前
2026年全国青少年信息素养大赛复赛真题(算法应用主题赛C++初中组:带解析)(7月11日试卷一)【文末附答案和解析】
c++·真题·答案·复赛·2026年·青少年信息素养大赛·算法应用主题赛
Hesionberger1 小时前
动态规划与二分法破解最长递增子序列
java·数据结构·python·算法·leetcode
imuliuliang1 小时前
关于Kruskal 算法在图优化问题中的扩展应用7
算法
大阿明1 小时前
C++智能指针与RAII机制精讲:彻底根治内存泄漏与野指针
java·jvm·c++