算法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。

例:used[1][2]=1,表示maze[1][2]已经走过。

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

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

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

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

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

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

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

例:当前坐标为(1,2),向上移动就是:(1+fx[0][0],2+fx[0][1]),得到(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/

相关推荐
苍天饶过谁?2 分钟前
C++模板类与继承
c++
PPPPPaPeR.27 分钟前
TopK问题与堆排序
c语言·开发语言·c++·算法
人才程序员42 分钟前
【Rust入门】猜数游戏
开发语言·c++·后端·单片机·游戏·rust·c
卡戎-caryon2 小时前
【数据结构】06.栈&&队列
c语言·数据结构·算法·链表
山脚ice2 小时前
【CT】LeetCode手撕—704. 二分查找
算法·leetcode
国中之林2 小时前
【qt】如何获取网卡的IP地址?
服务器·c++·qt·网络协议·学习·tcp/ip
贱贱的剑2 小时前
【算法】选择排序
算法·rust·排序算法
瑜陀2 小时前
2024.06.30 刷题日记
数据结构·算法·leetcode
Star Patrick2 小时前
*算法训练(leetcode)第二十天 | 39. 组合总和、40. 组合总和 II、131. 分割回文串
c++·算法·leetcode
光久li2 小时前
【算法刷题 | 动态规划14】6.28(最大子数组和、判断子序列、不同的子序列)
算法·动态规划