题目描述
有一个mn格的迷宫(表示有m行、n列),其中有可走的也有不可走的,如果用1表示可以走,0表示不可以走,文件读入这mn个数据和起始点、结束点(起始点和结束点都是用两个数据来描述的,分别表示这个点的行号和列号)。现在要你编程找出最短的道路,要求所走的路中没有重复的点,走时只能是上下左右四个方向。如果一条路都不可行,则输出相应信息(用-l表示无路)。
优先顺序:左上右下
输入输出格式
输入格式:
第一行是两个数m,n(1<m,n<15),接下来是m行n列由1和0组成的数据,最后两行是起始点和结束点。
输出格式:
字典序最小的路径,描述一个点时用(x,y)的形式,除开始点外,其他的都要用 “->”表示方向。 如果没有一条可行的路则输出-1。
输入输出样例
输入样例#1:
5 6
1 0 0 1 0 1
1 1 1 1 1 1
0 0 1 1 1 0
1 1 1 1 1 0
1 1 1 0 1 1
1 1
5 6
输出样例#1:
(1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(2,5)->(3,5)->(4,5)->(5,5)->(5,6)
//快走迷宫 洛谷
#include <cstdio>
using namespace std;
int n,m;
int c[1000][1000],heng[1000],zong[1000],pre[1000];
int vis[1000][1000];
int head,tail;
int sx,sy,ex,ey,flag = head = tail = 0;
int dir[4][2] = { {0,-1},{-1,0},{0,1},{1,0}};//左上右下
void print(int f)
{
if(pre[f]) print(pre[f]);//继续找前面的,知道找到pre为0的,即为f==1的
if(f == 1) printf("(%d,%d)" ,heng[f],zong[f]);
else printf("->(%d,%d)",heng[f],zong[f]);
// printf("\n");
}
void bfs(int x ,int y)
{
flag = 0;
vis[x][y] = 1;
tail = 1;
head = 0;
heng[tail] = x , zong[tail] = y , pre[tail] = 0;
while(head != tail)
{
head ++;//新的起始点
//printf("%d\n",head);
for(int k =0; k < 4;k ++)//沿四个方向 开始走
{
int xx = heng[head] + dir[k][0];
int yy = zong[head] + dir[k][1];
if(xx >= 1 && xx <= n && yy >= 1 && yy <= m)//限制不能超出棋盘大小
{
if(!vis[xx][yy])
{
vis[xx][yy] = 1;
tail ++;//将所有符合条件的都放入队列中,
// printf("%d\n",tail);
heng[tail] = xx, zong[tail] = yy ,pre[tail] = head;
if(xx == ex && yy == ey)
{
flag = 1;
print(tail);//从最尾端开始看,找最原始的哪一个点,然后输出
break;//打破for循环
}
//如果没到,那么继续进行for循环,知道结束,若结束还没有找到,==没有一条路径
}
}
}
if(flag) break;
}
}
int main()
{
scanf("%d%d",&n,&m);//n为行,m为列
for(int i = 1; i <= n;i++)
for(int j =1; j <= m;j++)
{
scanf("%d",&c[i][j]);
if(c[i][j] == 0) vis[i][j] = 1;//0代表不能走,所以这个点 不能在进行占领
}
scanf("%d%d%d%d",&sx,&sy,&ex,&ey);
bfs(sx,sy);
if(flag == 0) printf("-1\n");
return 0;
}
思路解析:
迷宫里最短路径问题:要用广搜 --- 很好理解:eg.先从起点开始走,先看周围一圈有没有终点,如果有的话,那么直接走到终点。
广搜(bfs)难点:要用一个head , tail 来表示现在遍历到哪了,head 表示节点,tail 表示扩展点。
1、先将head ,tail ,pre 要初始化,因为要输出,所以用一个前驱数组pre[100]记录一下移动之前的那个head(节点)。
2、使用while 循环,当这个队列中有东西的时候才开始进行(head != tail)head的初始值为0,要将head++ 变为现有的tail,然后这个点进行扩展,现在的这个点不可以超出棋盘的范围,但也不可以是0(0代表不能走),先对一个点的扩展点进行搜索,若都不是终点,那么要将每一个扩张点变为新的head(节点),再进行扩展,要将扩展点都放入队列之中,所以 tail要 ++,并改记录这个点的前驱(作为标记,方便之后的输出),用两个数组分别记录走到的点的横纵坐标,同时表示这个点已经被占领了。
3、如果现在这个点已经是终点了,那么就需要打印(方法比较特殊),并且立一个flag 表示可以找到一条路径。
4、print()打印的函数:输出迷宫的路径要从头到尾,但我们要输出的时候,最后一个点是终点,所以用pre数组记录之前的点的标号(head++相当于给每一个节点标了号),当pre数组所代表的数为1时,说明这个点是起点,从这个数组开始输出。
注意:1、这个print函数是一个递归函数,(1)先找起点(2)找到起点之后,输出起点(3)输出起点之后退出一层循环,再继续输出,知道结束。(所以格式腰围 if if else)
2、行走的方向要注意行和列:向左或向右时,行不变,列变,即x不变,y变
向上或向下时,行变,列不变,即x变,y不变