(DFS)迷宫问题

版权声明:所有内容仅供大家学习与复习使用,请勿用于任何商业用途;维特根斯坦曾说过,凡可说的,皆无意义。凡有意义的,皆不得不以荒唐的语言传递其意义。我十分赞同。 https://blog.csdn.net/qq_40828914/article/details/88413637

题目:

从s到t,.意味着可以走,*意味着不能走,如果能走,输出路径,如果不能走,输出no
输入:

5 6
....S*
.***..
.*..*.
*.***.
.T....

输出:

....m*                                                                        
.***mm                                                                        
.*..*m                                                                        
*.***m                                                                        
.Tmmmm  

分析与解答

1.整体思路:
我们先从第一个走的,因此先把第一个格子标记为已走过,然后开始走第一个格子上下左右的相邻格子,如果这个相邻格子没走过,而且从这个相邻格子能走到下一个格子,那么就可以一直走下去,继续调用dfs。

当调用dfs直到无法走下一个格子,这时直接返回false,然后回到上一个格子,直到返回到最开始的那个格子,此时最开始的格子走另一个方向的格子。然后继续重复之前的调用,直到找到终点。

2.推广到dfs的研究方法:
1.处理搜索结束时的边界条件

2.搜索:我们判断是否能继续往下搜,需要满足下一个方向的坐标在地图里面,而且没有被走过,而且没遇到墙。如果满足这些条件,就再调用dfs,继续往下搜。

3.回溯:当所有方向都搜完,也没找到路径,此时函数运行结束,这一层返回false,返回上一层的递归。返回之前,我们需要把访问时做的标记还原。返回上一层有可能就是改变方向继续进行新的搜索,直到达到边界条件。

如果说最后到了边界条件,那么底层的返回true,然后依次往上全部返回true,这样dfs(x,y)就返回true。就说明从起始点xy走是可以到终点的。

如果说一直都没有到边界条件,那么也就是说我们一开始把起始点设定为已经走过的点,然后上下左右搜索最后全部都回来了,此时第一层中那几个if都用完了,于是就继续往下走,也就是说,把起始的那个格子给初始化,并且返回false。

3.代码的简洁性:

然后由四个方向搜索,我们可以发现它有四个一摸一样的if,只不过每次的x和y的值有所不同,那么我们就可以引入一个方向变量,int dir[4][2]={{-1,0},{0,-1},{1,0},{0,1}}; 这样的话,我们就可以通过for循环和方向变量实现每次对xy的值的修改,然后再把if写道for里面,这样就不用把四个方向分开写了,简化了代码量。

代码

#include <iostream>
#include <string>
using namespace std;
int n, m;
string maze[110];
bool vis[110][110];
bool in(int x,int y){//防止走到地图外面
    return 0<=x&&x<n&&0<=y&&y<m;
}
bool dfs(int x,int y){//x代表行,y代表列
	if(maze[x][y]=='T'){
        return true;
    }
    vis[x][y]=1;//标记当前点已走过
    maze[x][y]='m';//走过的点用字符m标记
    int tx = x-1,ty=y;//向上搜
    if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){
        if(dfs(tx,ty)){
			return true;
        }
    }
    tx=x,ty=y-1;//左
    if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){
        if(dfs(tx,ty)){
			return true;
        }
    }
    tx=x+1,ty=y;//下
    if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){
        if(dfs(tx,ty)){
			return true;
        }
    }
    tx=x,ty=y+1;//右
    if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){
        if(dfs(tx,ty)){
			return true;
        }
    }
    vis[x][y]=0;
    maze[x][y]='.';
    return false;
}
int main() {
    // 输入迷宫地图
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        cin >> maze[i];
    }
    int x,y;//确定起始位置
    for(int i=0;i<n;++i){
		for(int j=0;j<m;++j){
            if(maze[i][j]=='S'){
                x=i,y=j;
            }
        }
    }
    if(dfs(x,y)){
		for(int i=0;i<n;++i){
            cout<<maze[i]<<endl;
        }
    }
    else{
		cout<<"NO!"<<endl;
    }
    return 0;
}

用二维数组将上下左右搜索整合到一个for循环里。

#include <iostream>
#include <string>
using namespace std;
int n, m;
string maze[110];
bool vis[110][110];
int dir[4][2]={{-1,0},{0,-1},{1,0},{0,1}};
bool in(int x, int y) {
    return 0 <= x && x < n && 0 <= y && y < m;
}
bool dfs(int x, int y) {
    if(maze[x][y] == 'T'){
        return true;
    }
    vis[x][y]=1;
    maze[x][y]='m';
    for(int i=0;i<4;++i){
        int tx=x+dir[i][0];
        int ty=y+dir[i][1];
        if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){
            if(dfs(tx,ty)){
                return true;
            }
        }
    }
    vis[x][y]=0;
    maze[x][y]='.';
    return false;
}
int main() {
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        cin >> maze[i];
    }
    int x, y;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (maze[i][j] == 'S') {
                x = i, y = j;
            }
        }
    }
    if (dfs(x, y)) {
        for (int i = 0; i < n; i++) {
            cout << maze[i] << endl;
        }
    } else {
        cout << "NO!" << endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40828914/article/details/88413637