[C] 不撞南墙不回头——深度优先搜索

深度优先搜索DFS的概念

  • dfs:Depth First Search
  • 理解深度优先搜索的关键在于解决当下该如何做。至于下一步该如何做则与当下该如何做是一样的。
  • 通常的方法就是把每种可能都去尝试一遍(一般用for来遍历)
  • 当解决完这一步之后就dfs(step+1)
  • 下一步的解决方法和这一步是完全一样的
以下给出dfs的基本模型

(基于C语言实现)

void dfs(int step){
	判断边界
	尝试每一种可能for(i=1;i<=n;i++){
		继续下一步dfs(step+1);
	}
	返回
}
  • 每一种尝试都是一种拓展,但并不是每一种拓展都会成功。(在尝试的时候加上条件判断)

用一个简单的枚举理解一下

#include<stdio.h>

int a[10], book[10], n;

void dfs(int step)
{
    int i;
    if (step == n + 1)
    {
        for (i = 1; i <= n; i++)
        {
            printf("%d ", a[i]);
        }
        printf("\n");
        return;
    }

    for (i = 1; i <= n; i++)
    {
        if (book[i] == 0) // 表示这个点还没有被访问过
        {
            a[step] = i; // 将这个点加入到已访问过的队列中
            book[i] = 1; // 此时这个点就被访问过了~

            dfs(step + 1); // 步数+1,继续下一个dfs
            book[i] = 0; // 将这个点标记为未访问过(可以理解为book[i]的改变要夹住中间的dfs调用。)
        }
    }
    return;
}


int main()
{

    n = 4;
    dfs(1);

    return 0;
}

运行结果

在这里插入图片描述


例题

解救小哈

题目描述

有一天,小哈一个去玩迷宫。但是方向感很不好的小哈很快就迷路了。小哼得知后便立即去解救无助的小哈。小哼当然是有备而来,已经弄清楚了迷宫地图,现在小哼要以最快速度去解救小哈。问题就此开始了…… 迷宫由n×m列的单元格组成,每个单元格要么是空地,要么是障碍物。你的任务是帮助小哼找到一条从迷宫的起点到小哈所在位置的最短路径,注意障碍物是不能走的,当然也不能走到迷宫之外。

输入

第一行有两个数n 和m。n表示迷宫的行,m表示迷宫的列。接来下来n行m列为迷宫,0表示空地,1表示障碍物。最后一行4个数,前两个数为迷宫入口的x和y坐标。后两个为小哈的x和y坐标。
0≤n,m≤100

输出

一个整数表示小哼到小哈的最短步数。如果不能解救小哈则输出No Way!
在这里插入图片描述

样例输入1
5 4
0 0 1 0
0 0 0 0
0 0 1 0
0 1 0 0
0 0 0 1
1 1 4 3
样例输出1
7
样例输入2
3 3
1 1 1 
0 1 0 
0 1 0 
2 1 3 3
样例输出2
No Way!

题解

这个题解是我自己写的,不是《啊哈算法》里面老师给的代码喔。

主要是结构体这个东东我不太喜欢,而且我觉得我写的代码更好理解(bushi)

在这个代码里可以很直观地观察出前文提到的关于用book数组book[x][y] = 1;book[x][y] = 0;夹住下一步的dfs的想法。

这次手撕DFS让我发现了这个算法的问题:空间复杂度较高。需要一直保持一个book数组来记录每个点的状态,这就导致开数组的时候不能开太大了(不能开3000*3000),遇到0<n,m<3000的题目的时候可能需要另寻出路?

DFS属于盲目搜索,最糟糕的情况算法时间复杂度为O(!n)。所以遇到二维数组不大,但是容易爆T的题目的时候可以优先考虑DFS。

#include<stdio.h>

int a[1000][1000], book[1000][1000], n, m, x1, x2, y1, y2;
int min = 999999;

void dfs(int x, int y, int step)
{
    int i;

    if (x > n || y > m || x <= 0 || y <= 0)
    {
        return;
    }
    if (x == x2 && y == y2)
    {
        if (step < min)
            min = step;
        return;
    }

    else
    {
        book[x][y] = 1;
        if (a[x + 1][y] == 0 && book[x + 1][y] == 0)
        {
            book[x][y] = 1;
            dfs(x + 1, y, step + 1);
            book[x][y] = 0;

        }
        if (a[x][y + 1] == 0 && book[x][y + 1] == 0)
        {
            book[x][y] = 1;
            dfs(x, y + 1, step + 1);
            book[x][y] = 0;

        }
        if (a[x - 1][y] == 0 && book[x - 1][y] == 0)
        {
            book[x][y] = 1;
            dfs(x - 1, y, step + 1);
            book[x][y] = 0;

        }
        if (a[x][y - 1] == 0 && book[x][y - 1] == 0)
        {
            book[x][y] = 1;
            dfs(x, y - 1, step + 1);
            book[x][y] = 0;
        }
        book[x][y] = 0;
    }
    return;
}


int main()
{
    scanf("%d %d", &n, &m);
    int i, j;
    for (i = 1; i <= n; i++)
    {
        for (j = 1; j <= m; j++)
        {
            scanf("%d", &a[i][j]);
        }
    }
    scanf("%d %d %d %d", &x1, &y1, &x2, &y2);

    dfs(x1, y1, 0);
    if (min == 999999)
    {
        printf("No Way!");
    }
    else
        printf("%d", min);
    return 0;
}

运行结果:
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值