迷宫寻路问题全解 DFS、BFS、A*

1、深度优先搜索(DFS)+回溯

最基本的板子:

void DFS(int x,int y)
{
    if (x,y都与目标点相同)
    {
        得到一个解;
    }
    else
    {
        for (int i = 1; i <= 四个方向; i++)
            if (满足进一步搜索条件)
            {
                为进一步搜索所需要的状态打上标记;
                DFS(to_x, to_y);
                恢复到打标记前的状态;//也就是回溯一步
            }
    }
}

适用类型①:求可行解数量

https://www.luogu.org/problemnew/show/P1605

#include <iostream>
using namespace std;

int direction[4][2] = { {-1,0},{1,0},{0,-1},{0,1} };//上下左右
int maze[10][10];
int N, M, T, cnt, SX, SY, FX, FY;

void DFS(int x, int y) {
	if (x < 1 || x > N || y < 1 || y > M) return;
	if (x == FX && y == FY) {
		cnt++;
		return;
	}
	for (int i = 0; i < 4; i++) {
		int to_x = x + direction[i][0], to_y = y + direction[i][1];
		if (maze[to_x][to_y] != 1) {
			maze[to_x][to_y] = 1;
			DFS(to_x, to_y);
			maze[to_x][to_y] = 0;
		}
	}
}
int main() {
	cin >> N >> M >> T;
	cin >> SX >> SY >> FX >> FY;
	for (int i = 0; i < T; i++) {
		int x, y;
		cin >> x >> y;
		maze[x][y] = 1;
	}
	maze[SX][SY] = 1;
	DFS(SX, SY);
	cout << cnt << endl;
	return 0;
}

适用类型②:输出所有可行解

例题:https://www.luogu.org/problemnew/show/P1238

这类题目需要注意的是,要知道搜索前进方向的顺序,一般会直接写出来的,没写的话,可以根据题目样例去判断。

#include <iostream>
using namespace std;

//上左右下
int direction[4][2] = { {-1,0},{0,-1},{0,1},{1,0} };

int m[20][20];
int path[250][2];

int N, M, T, cnt;
int SX, SY, EX, EY;

void DFS(int x, int y,int k) {
    if (x < 1 || x > M || y < 1 || y > N) return;
    if (x == EX && y == EY) {
        cnt++;
        for (int i = 0; i < k; i++) {
            cout << "(" << path[i][0] << "," << path[i][1] << ")";
            if (i != k - 1)cout << "->";
        }
        cout << endl;
        return;
    }

    for (int i = 0; i < 4; i++) {
        int tox = x + direction[i][0], toy = y + direction[i][1];
        if (m[tox][toy] == 1) {
            m[tox][toy] = 0;
            path[k][0] = tox;
            path[k][1] = toy;
            DFS(tox, toy, k + 1);
            m[tox][toy] = 1;
        }
    }
}
int main() {
    cin >> M >> N;
    for (int i = 1; i <= M; i++) {
        for (int j = 1; j <= N; j++) {
            cin >> m[i][j];
        }
    }
    cin >> SX >> SY >> EX >> EY;
    m[SX][SY] = 0;
    path[0][0] = SX, path[0][1] = SY;
    DFS(SX, SY, 1);
    if (cnt == 0)cout << "-1" << endl;
    return 0;
}

注意:

  • m , n m , n m,n 较大时,无法胜任,最多在15左右就不行了(还得是迷宫中障碍的位置比较配合的情况,一般大于10,就要慎重考虑该不该用DFS了)。
  • 搜索前进方向的顺序是可能会影响到效率的,如果起点在左上部分,终点在右下部分,理想情况下,优先选择右方向和下方向,可以根据起点和终点的位置关系,调整方向数组的顺序,效率会更高。
  • 找到的第一条解,不一定是最短的,应该说一般都不是。

2、广度优先搜索(BFS)

适用类型①:最短路径的长度

题目链接:走迷宫

#include <iostream>
#include <queue>
#include <fstream>

using namespace std;

struct Coord {
	int x, y, level;
	Coord(int x, int y, int level) :x(x), y(y), level(level) {};
};
char maze[15][15];
int direction[4][2] = { {-1,0},{1,0},{0,-1},{0,1} };
int SX = 1, SY = 2, FX = 10, FY = 9;

int BFS() {
	queue<Coord>q;
	maze[SX][SY] = '#';
	q.push(Coord(SX, SY, 0));
	while (!q.empty()) {
		Coord p = q.front(); q.pop();
		int x = p.x, y = p.y;
		for (int i = 0; i < 4; i++) {
			int tox = x + direction[i][0], toy = y + direction[i][1];
			if (tox == FX && toy == FY)
				return p.level + 1;
			//这里也可以在迷宫边上加一圈全是 '#' 的边界
			if (maze[tox][toy] == '.' && (tox >= 1 && tox <= 10 && toy >= 1 && toy <= 10)) {
				maze[tox][toy] = '#';
				q.push(Coord(tox, toy, p.level + 1));
			}
		}
	}
	return -1;
}

int main() {
	while (true) {
		for (int i = 1; i <= 10; i++) {
			for (int j = 1; j <= 10; j++) {
				if (scanf("%c", &maze[i][j]) == EOF) return 0;
			}
			getchar();
		}
		int res = BFS();
		cout << res << endl;
	}
	return 0;
}

适用类型②:找到最短的一条路径

题目链接:http://poj.org/problem?id=3984

注意:

①:BFS搜到的第一条一定是最短的,但是最短的不一定只有一条。题目未说明有唯一解的,要注意题目对解的要求。

②:BFS没有办法想DFS那样直接把路径存下来;只能把每个点的前驱记下来,这样最后到了终点,得到的路径正好是是反过来的。两种选择,1、自行处理倒过来输出。2、BFS直接倒过来搜,即从终点向起点搜,负负得正

#include <iostream>
#include <queue>
using namespace std;

#define PAIR make_pair
//上下左右
int direction[4][2] = { {-1,0},{1,0},{0,-1},{0,1} };

int m[20][20];
int path[15][15][2];

void BFS(int x, int y) {
    queue<pair<int, int>>q;
    q.push(PAIR(x, y));
    while (!q.empty()) {
        x = q.front().first; y = q.front().second;
        q.pop();
        for (int i = 0; i < 4; i++) {
            int tox = x + direction[i][0], toy = y + direction[i][1];
            if (m[tox][toy] == 0 && (tox >= 0 && tox <= 4 && toy >= 0 && toy <= 4)) {

                if (tox == 0 && toy == 0) {
                    cout << "(" << 0 << ", " << 0 << ")" << endl;
                    while (!(x == 4 && y == 4)) {
                        cout << "(" << x << ", " << y << ")" << endl;
                        int from_x = path[x][y][0];
                        int from_y = path[x][y][1];
                        x = from_x; y = from_y;
                    }
                    cout << "(" << 4 << ", " << 4 << ")" << endl;
                    return;
                }

                m[tox][toy] = 1;
                path[tox][toy][0] = x;
                path[tox][toy][1] = y;
                q.push(PAIR(tox, toy));
            }
        }
    }
}
int main() {
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 5; j++) {
            cin >> m[i][j];
        }
    }
    m[4][4] = 0;
    BFS(4, 4);
    return 0;
}

优化:双向BFS

正向BFS,与反向BFS用不同的值去标记地图,第一次相遇时(某一方发现对方的标记值),一定是一条最短的路径。这个时候,path中记录着两段方向相反的路径,输出的时候需要处理。

同样的,如果题目要求是按照某种顺序、优先某个方向;那么反向的BFS只要反着来就行了。

#include <iostream>
#include <stack>
#include <queue>
using namespace std;

#define PAIR make_pair

int direction[4][2] = { {-1,0},{1,0},{0,-1},{0,1} };

int m[20][20];
int path[15][15][2];
bool flag = false;
int SX = 0, SY = 0, EX = 4, EY = 4;
void move_one_step(queue<pair<int,int>>&q,int sign) {
    int x = q.front().first, y = q.front().second; q.pop();
    for (int i = 0; i < 4; i++) {
        int tox = x + direction[i][0], toy = y + direction[i][1];
        if (m[tox][toy] == 0 && (tox >= SX && tox <= EX && toy >= SX && toy <= EY)) {
            m[tox][toy] = sign;
            path[tox][toy][0] = x;
            path[tox][toy][1] = y;
            q.push(PAIR(tox, toy));
        }
        //发现对方标记值
        else if (m[tox][toy] == -sign) {
            int tempx = tox, tempy = toy;
            /*输出 起点 到相遇点*/
            stack<pair<int, int>>s;
            while (!(tox == 0 && toy == 0)) {
                int t1 = tempx, t2 = tempy;
                s.push(PAIR(tempx, tempy));
                tempx = path[t1][t2][0];
                tempy = path[t1][t2][1];
            }
            while (!s.empty()) {
                int xx = s.top().first, yy = s.top().second;
                s.pop();
                cout << "(" << xx << ", " << yy << ")" << endl;
            }
            /*----------------*/
            /*从相遇点到终点*/
            tempx = x, tempy = y;
            while (!(tempx == 4 && tempy == 4)) {
                int t1 = tempx, t2 = tempy;
                cout << "(" << tempx << ", " << tempy << ")" << endl;;
                tempx = path[t1][t2][0];
                tempy = path[t1][t2][1];
            }
            flag = true;
            return;
        }
    }
}

void BFS(queue<pair<int, int>>&f, queue<pair<int, int>>&r) {
    int i = 1;
    while ((!f.empty() || !r.empty()) && !flag) {
        if (i & 1)
            move_one_step(f, 2);
        else
            move_one_step(r, -2);
        i++;
    }
}
int main() {
    queue<pair<int, int>>f, r;

    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 5; j++) {
            cin >> m[i][j];
        }
    }
    f.push(PAIR(SX, SY));
    r.push(PAIR(EX, EY));
    m[EX][EY] = -2;
    m[SX][SY] = 2;
    cout << "(" << SX << ", " << SY << ")" << endl;
    BFS(f, r);
    cout << "(" << EX << ", " << EY << ")" << endl;
    return 0;
}

3、A*搜索

A找到的第一个解不一定是最短的,所以A不能用来去找最短路径(在有多解的情况下);

A*的搜索特性限制,如果用来输出所有可行解,就没有使用的意义了。

所以其实迷宫题并不适合用A*解,除非题目要求比较特殊(只有一条路),仅做参考。

采取 h ( n ) = ∣  end.  x − n ⋅ x ∣ + ∣  end.  y − n ⋅ y ∣ \mathrm{h}(\mathrm{n})=\mid \text { end. } \mathrm{x}-\mathrm{n} \cdot \mathrm{x}|+| \text { end. } \mathrm{y}-\mathrm{n} \cdot \mathrm{y} \mid h(n)= end. xnx+ end. yny

适用类型:找唯一的路径

#include <iostream>
#include <queue>
using namespace std;

#define PAIR make_pair
//上下左右
int direction[4][2] = { {-1,0},{1,0},{0,-1},{0,1} };
int sx = 0, sy = 0, ex = 4, ey = 4;
class Node {
public:
    int x, y, g, h;
    Node(int x, int y,int g){
        this->x = x;
        this->y = y;
        this->g = g;
        h = abs(ex - x) + abs(ey - y);
    }
    bool operator<(Node n)const {
        return n.g + n.h < g + h;
    }
};

int m[20][20];
int path[15][15][2];
int head = 0, tail = 1;
int cnt[255];

void BFS(int x, int y) {
    priority_queue<Node>q;
    //queue<pair<int, int>>q;
    q.push(Node(x, y, cnt[head]));

    while (!q.empty()) {
        x = q.top().x; y = q.top().y;
        q.pop();
        if (x == 0 && y == 0) {
            while (true) {
                cout << "(" << x << ", " << y << ")" << endl;
                if (x == 4 && y == 4)break;
                int from_x = path[x][y][0];
                int from_y = path[x][y][1];
                x = from_x; y = from_y;
            }
            return;
        }
        for (int i = 0; i < 4; i++) {
            int tox = x + direction[i][0], toy = y + direction[i][1];
            if (m[tox][toy] == 0 && (tox >= 0 && tox <= 4 && toy >= 0 && toy <= 4)) {
                cnt[tail++] = cnt[head] + 1;
                m[tox][toy] = 1;
                path[tox][toy][0] = x;
                path[tox][toy][1] = y;
                q.push(Node(tox, toy, cnt[head] + 1));
            }
        }
        head++;
    }
}
int main() {
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 5; j++) {
            cin >> m[i][j];
        }
    }
    m[4][4] = 0;
    BFS(4, 4);
    return 0;
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在解决迷宫问题时,可以使用搜索算法,如广度优先搜索或深度优先搜索。 首先,建立一个二维数组来表示迷宫,其中 1 表示墙壁,0 表示可以通行的。然后,从起点开始,按照搜索算法的顺序(如广度优先搜索就是按照层级顺序,深度优先搜索就是按照深度顺序)依次搜索与当前点相邻的点,如果发现终点就找到了出,否则继续搜索。 下面是一个简单的 c 语言代码示例,实现了使用广度优先搜索解决迷宫问题: ``` #include <stdio.h> #include <stdlib.h> #define MAX_SIZE 100 // 最大迷宫大小 #define MAX_STEPS 10000 // 最多走的步数 // 迷宫地图,1 为墙壁,0 为可以通行的 int maze[MAX_SIZE][MAX_SIZE]; // 记录每个位置是否已经走过 int visited[MAX_SIZE][MAX_SIZE]; // 起点和终点的坐标 int startX, startY, endX, endY; // 四个方向的移动偏移量,表示从当前点往四个方向走一步的新坐标 int dx[4] = {1, 0, -1, 0}; int dy[4] = {0, 1, 0, -1}; // 队列,用于存 ### 回答2: 迷宫问题是一个常见的算法问题,在C语言中可以使用深度优先搜索(DFS)或广度优先搜索(BFS)来解决。 首先,我们需要将迷宫的地图转化为程序中能够处理的数据结构,比如使用二维数组来表示迷宫的格子。迷宫的墙壁可以用1表示,可以用0表示。 然后,我们可以使用DFSBFS算法来遍历迷宫,从起点开始搜索,直到找到终点为止。搜索时需要考虑迷宫的边界和墙壁,避免越界和进入墙壁。 以DFS为例,具体步骤如下: 1. 创建一个栈用于存储当前的径。 2. 将起点入栈,并将起点标记为已访问。 3. 循环执行以下步骤,直到找到终点或栈为空: - 取出栈顶元素作为当前的位置。 - 如果当前位置是终点,则表示找到了一条径,输出该径并结束。 - 否则,遍历当前位置的相邻格子,如果某个相邻格子未访问且不是墙壁,则将其入栈并标记为已访问。 4. 如果栈为空仍未找到径,则表示没有可行径。 需要注意的是,为了保证找到的径是最短径,可以在搜索过程中记录每个格子所在的径。当找到终点时,回溯该径即可得到最短径。 通过以上步骤,我们可以用C语言编写程序解决迷宫问题。 ### 回答3: C语言是一种非常强大的编程语言,可以用来解决各种问题,包括迷宫问题迷宫问题是指在一个迷宫中找到从起点到终点的径。在解决这个问题时,可以使用C语言的数据结构和算法来实现。 首先,我们可以使用二维数组来表示整个迷宫,其中不可通行的地方可以标记为墙壁,可以通行的地方可以标记为径。 然后,我们可以使用递归的方式来搜索径。从起点开始,我们可以先判断当前位置是否为终点,如果是的话,说明已经找到了径,可以返回。如果不是终点,我们可以继续向上、下、左、右四个方向进行搜索,只要该方向是可通行的,并且未走过,就可以继续递归搜索。在搜索时,我们可以使用一个标记数组,用来记录哪些位置已经走过,防止重复走。 当搜索到某个位置时,如果四个方向都无法通行,说明该位置是死,需要返回上一层的递归。 最后,当搜索到终点时,我们就找到了一条径,可以将径记录下来,并输出结果。 通过以上的步骤,我们就可以使用C语言解决迷宫问题。这只是其中一种解决方法,还可以使用其他的数据结构和算法来实现,具体可以根据实际情况进行选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值