算法竞赛-走迷宫

来源:互联网 发布:程序员很难吗 编辑:程序博客网 时间:2024/05/16 19:42
一个网格迷宫由n行m列的单元格组成,每个单元格要么是空地(用1表示)要么是障碍物
(用0表示)。任务是找一条从起点到终点的最短移动序列,其中UDLR代表上下左右移动到
相邻单元格。任何时候都不能在障碍物格中,也不能走到迷宫之外。起点和终点保证是空地
n,m<=100;


图的bfs与树的bfs一样,但需要避免重复访问一个节点。下面代码用标
记vis[x][y]记录格子(x,y)是否走过,和dfs一样


样例输入:6行5列,入口(0,0),出口(0,4)
6 5      
1 1 0 1 1
1 0 1 1 1
1 0 1 0 0
1 0 1 1 1
1 1 1 0 1

1 1 1 1 1

#include<iostream>#include<queue>#include<string>#include<string>using namespace std;const int MAXN = 105;int dx[] = { -1,1,0,0 }, dy[] = { 0,0,-1,1 };//dx上下,dy左右char name[] = { 'U','D','L','R' };int q[MAXN*MAXN];//队列,保存当前结点编号int vis[MAXN][MAXN], nMap[MAXN][MAXN];int dir[MAXN*MAXN];int fa[MAXN][MAXN], dist[MAXN][MAXN], last_dir[MAXN][MAXN];int n, m;//行、列数void funInit();void bfs(int x, int y);void funcInput();void print_path(int x,int y);int main(){cin >> n >> m;
for (int i = 0; i < n; i++)for (int j = 0; j < m; j++){cin >> nMap[i][j];}
        memset(vis, 0, sizeof(vis));//给初始数组清零memset(dist, 0, sizeof(dist));//给初始数组清零
bfs(0,0);//起始点print_path(0, 4);//走出点system("pause");return 0;}void bfs(int x,int y){int front = 0, rear = 0;int u = x*m + y;vis[x][y] = 1;fa[x][y] = u;//起点元素的父亲为它本身dist[x][y] = 0;q[rear++] = u;//第一个元素入队while (front < rear){u = q[front++];//第一个元素出队x = u / m; y = u%m;for (int d = 0; d < 4; d++)//把出队结点上下左右可以访问的元素依次入队{int nx = x + dx[d], ny = y + dy[d];if (nx >= 0 && nx < n&&ny >= 0 && ny < m&&nMap[nx][ny] && !vis[nx][ny])//在数据范围内并且不是障碍且未被访问过{int v = nx*m + ny;q[rear++] = v;//自身入队vis[nx][ny] = 1;//标记为被访问fa[nx][ny] = u;//*********设置当前点的父节点,存入数组中,为接下来寻找路径做准备*********dist[nx][ny] = dist[x][y] + 1;//距离在父节点的基础上加1last_dir[nx][ny] = d;//记录父节点到子节点的移动方向}}}}/*为方便起见我们把格子从上到下编号为0 1 2 3 4 ...n*m,因此第i行第j个格子的编号为i*m+j,而编号为u的行号为u/m,列号为u%m。当格子(x,y)扩展出格子(nx,ny)后,我们不仅需要更新dist[nx][ny]=dist[x][y]+1还要保存新格子(nx,ny)的父亲编号fa[nx][ny]以及父节点到它的移动方向last_dir[nx][ny].有了这两个值,就可把路径打印出来了。*///void print_path(int x,int y)//{// int fx = fa[x][y] / m;// int fy = fa[x][y] % m;// if (fx != x || fy != y)// {// print_path(fx, fy);// putchar(name[last_dir[x][y]]);// }//}/* 这里用到了递归的技巧:如果格子(x,y)有父亲(fx,fy),需要先打印从起点到(fx,fy)的最短路,然后打印(fx,fy)到(x,y)的移动方向,也就是last_dir[x][y]所对应的方向名字。这个函数非常方便,请当心一点:n和m太大时可能会产生栈溢出,需要改写成下面的非递归形式*/void print_path(int x, int y){int c = 0;for (;;){//开始时从指定的点初始 得到fx fy。之后根据该点存储的父节点依次得到其他的fx fy int fx = fa[x][y] / m;int fy = fa[x][y] % m;if (fx == x && fy == y)break;dir[c++] = last_dir[x][y];//把移动方向赋值给数组dir,以便输出路径x = fx;y = fy;}while (c--)putchar(name[dir[c]]);cout << "最短路径长度为:" << dist[0][4] << endl;}




0 0
原创粉丝点击