目录
前言
求最短路径是一个典型的图论问题,但它也分为两种情况
相邻节点距离相同
由于bfs本身的搜索方式就是一圈一圈向外扩展,这个问题用普通的bfs就行了,因为最先扩展到的到达终点的路径肯定就是最短路
相邻节点距离不同
由于相邻节点距离不同此时就不能用常规的bfs,这会导致不同的方向扩散的距离不同,这就要使用dijkstra算法(每次先入队距离最短的路径)或其他更加通用的算法
打印路径
由于在一个图论问题中,最短路往往不是唯一的,所以一般不会要求打印路径,如果要打印,一般是打印字典序最小的路
迷宫
问题描述
给定一个迷宫,入口为左上角,出口为右下角,用一个30*50的数字网格代表迷宫,其中1表示障碍,0表示平地,要求只能上下左右一格一格在平地上移动,请找出一种路径最短的离开迷宫的方式,如果有多种答案,输出字典序最小的一种
输入
输入一格30*50的迷宫
输出
输出字典序最少,且路径最短的一种路径
备注:字典序中D<L<R<U
问题分析
本题是一个相邻节点距离相同的图论问题,所以用普通的bfs就可以完成,但是本题需要打印最短路径,所以需要一个数组pre[x][y]存储节点(x,y)的前驱节点。
打印路径方法
首先我们解决一下打印路径方法的问题,上面说到用一个数组pre[x][y]存储节点(x,y)的前驱节点,但存储一格节点数组太浪费空间,在这里我们这样处理,pre[x][y]=上一节点到达该节点的方法。例如:
这样我们就知道,(2,3)这个节点是由其前一个节点左移一格而来的,那么我们就可以很快得到(2,3)的前驱就是(2,4),然后再利用递归思想到(0,0)结束递归,就可以得出完整路径。
剪枝
明确的这点之后就可以进行剪枝了,由于本题相对简单,所以剪枝也就是只有两个常规剪枝,可行性剪枝,最优化剪枝
代码
#include<iostream>
#include<queue>
using namespace std;
char pre[40][60];
char mp[40][60];
bool vis[40][60];
int dir[5][2] = { {0,0}, { 1,0 },{0,-1},{0,1},{-1,0} };
char pos[5] = { 'O','D','L','R','U' };
struct node {
int x;
int y;
node() {}
node(int xx,int yy):x(xx),y(yy){}
};
queue<node>que;
node now, nxt;
void print_path(int x, int y) {
if (x == 1 && y == 1) return;
if (pre[x][y] == 'D') print_path(x - 1, y);
if (pre[x][y] == 'L') print_path(x, y + 1);
if (pre[x][y] == 'R') print_path(x, y - 1);
if (pre[x][y] == 'U') print_path(x + 1, y);
cout << pre[x][y]; //相当于二叉树的后序遍历,会将最后入栈的元素(起点)最先打印
}
void bfs() {
que.push(node(1, 1));
vis[0][0] = true;
while (!que.empty()) {
now = que.front();
if (now.x == 30 && now.y == 50) {
print_path(30, 50);
}
que.pop();
for (int i = 1; i <= 4; i++) {
int nx = now.x + dir[i][0], ny = now.y + dir[i][1];
if (nx < 1 || nx>30 || ny < 1 || ny>50 || mp[nx][ny]=='1') continue; //剪枝1
if (!vis[nx][ny]) { //剪枝2
vis[nx][ny] = true;
que.push(node(nx, ny));
pre[nx][ny] = pos[i];
}
}
}
}
int main() {
for (int i = 1; i <= 30; i++) {
for (int j = 1; j <= 50; j++) {
cin >> mp[i][j];
}
}
bfs();
return 0;
}