题目
学霸抢走了大家的作业,班长为了帮同学们找回作业,决定去找学霸决斗。但学霸为了不要别人打扰,住在一个城堡里,城堡外面是一个二维的格子迷宫,要进城堡必须得先通过迷宫。因为班长还有妹子要陪,磨刀不误砍柴功,他为了节约时间,从线人那里搞到了迷宫的地图,准备提前计算最短的路线。可是他现在正向妹子解释这件事情,于是就委托你帮他找一条最短的路线。
输入
第一行两个整数n, m,为迷宫的长宽。
接下来n行,每行m个数,数之间没有间隔,为0或1中的一个。0表示这个格子可以通过,1表示不可以。假设你现在已经在迷宫坐标(1,1)的地方,即左上角,迷宫的出口在(n,m)。每次移动时只能向上下左右4个方向移动到另外一个可以通过的格子里,每次移动算一步。数据保证(1,1),(n,m)可以通过。
输出
第一行一个数为需要的最少步数K。
第二行K个字符,每个字符∈{U,D,L,R},分别表示上下左右。如果有多条长度相同的最短路径,选择在此表示方法下字典序最小的一个。
样例输入
3 3
001
100
110
样例输出
4
RDRD
数据规模和约定
有20%的数据满足:1<=n,m<=10
有50%的数据满足:1<=n,m<=50
有100%的数据满足:1<=n,m<=500
解题思路
本题需要找到最短路径,且如果有相同长度路径,按照字母序排列。如果采用深度优先搜索(DFS),我们需要搜索所有可能路径,选择其中符合要求的一条;但如果采用广度优先搜索(BFS),只每一步都按照字母序(‘D’,‘L’,‘R’,‘U’)去走,找到第一条到达终点的路径即可。显然,后者效率更优,因此,采用BFS解题。
首先,本题在BFS中的每一步都需要记录步数、当前坐标以及路径(一个字符串),因此,采用结构体队列。接下来BFS仍然使用常规思路:首先初始化第一步存入队列,之后每一次取队列最前面的结点,走下一步,并将走过的结点从可以走的路径上“抹去”(设置为1),若到达终点,输出并停止BFS;反之,存入队列并依此循环。
易错点
- 读入时注意换行符,对每一个数字进行读入时用scanf(“%c”,&temp)或者getchar;
- 要避免走回头路;
- 本题要使用BFS而不是DFS,因为只需要广泛寻找,找到先到达终点的路即可停止继续搜索。
代码
#include<bits/stdc++.h>
using namespace std;
int F = 0;//表示是否找到第一个到终点的最短路径
int r,column;//行,列
char a[501][501];//邻接矩阵
char D[4] = {'D','L','R','U'};//下左右上的字母序
int dir[4][2] = {{1,0},{0,-1},{0,1},{-1,0}};//对应的坐标移动
struct node{
int x;
int y;
int steps;
string con;
};
queue <struct node> q;
void BFS(){
int i,tx,ty,nowx,nowy,lenc,step;
struct node N,temp;
string d;
for (i=0;i<4;i++)//第一步的初始化
{
tx = 1+dir[i][0];
ty = 1+dir[i][1];
if (tx>=1 && ty>=1 && ty<=column && tx<=r && a[tx][ty]==0)//能走
{
N.con = D[i];
N.x = tx;
N.y = ty;
N.steps = 1;
q.push(N);
a[tx][ty] = 1;
}
}
while (!q.empty()){
N = q.front();
nowx = N.x;
nowy = N.y;
q.pop();
for (i=0;i<4;i++)//四个方向
{
d = N.con;
tx = nowx+dir[i][0];
ty = nowy+dir[i][1];
if (tx>0 && tx<=r && ty>0 && ty<=column && a[tx][ty]==0)//能走
{
a[tx][ty] = 1;
d+=D[i];//加上新路径
if (tx==r && ty==column)//第一个到终点的
{
printf("%d\n",N.steps+1);
cout << d;
F = 1;
break;
}
else
{
temp.x = tx;
temp.y = ty;
temp.con = d;
temp.steps = N.steps+1;
q.push(temp);
}
}
}
if (F==1)
break;
}
}
int main()
{
int i,j;
char temp;
scanf("%d %d\n",&r,&column);//读入行列数
for (i=1;i<=r;i++)
{
for (j=1;j<=column;j++)
{
scanf("%c",&temp);
a[i][j] = temp-48;
}
getchar();//读入换行符
}
a[1][1] = 1;
BFS();
return 0;
}
错误代码
采用DFS,运行后时间超限(42分):
#include<bits/stdc++.h>
using namespace std;
int adj[501][501];
int r,c,Min=1000000;
int k = 0;//记录struct元素的数目
int dir[4][2] = {{1,0},{0,-1},{0,1},{-1,0}};//D,L,R,U
char D[4] = {'D','L','R','U'};//按照字母顺序排列
vector <char> q;
char roads[500];
int len_r=0;
void DFS(int x, int y){
int i,tx,ty,t;
if (x==r && y==c)//到达出口
{
t = q.size();
if (t<Min)//相等长度的将不再被替换,保证了按照字母顺序
{
Min = t;
for (len_r=0;len_r<Min;len_r++)
roads[len_r] = q[len_r];
roads[len_r] = '\0';
}
return ;
}
for (i=0;i<4;i++)//四个方向
{
tx = x+dir[i][0];
ty = y+dir[i][1];
if (tx>0 && tx<=r && ty>0 && ty<=c && adj[tx][ty]==0)//能走
{
adj[tx][ty] = 1;
q.push_back(D[i]);
if (Min>q.size())//如果当前已经比min长了,不必再DFS
DFS(tx,ty);
q.pop_back();//回溯
adj[tx][ty] = 0;
}
}
}
int main()
{
int i,j;
char temp;
scanf("%d %d\n",&r,&c);
for (i=1;i<=r;i++)
{
for (j=1;j<=c;j++)
{
scanf("%c",&temp);
adj[i][j] = temp-48;
}
getchar();//换行符
}
adj[1][1] = 1;
DFS(1,1);
printf("%d\n%s",len_r,roads);
return 0;
}
采用BFS+map,运行时间仍然超限(33分):
#include<bits/stdc++.h>
using namespace std;
int F = 0;//表示是否找到第一个到终点的最短路径
int r,column;//行,列
char a[501][501];//邻接矩阵
char D[4] = {'D','L','R','U'};//下左右上的字母序
int dir[4][2] = {{1,0},{0,-1},{0,1},{-1,0}};//对应的坐标移动
struct node{
int x;
int y;
int steps;
};
queue <string> q;
map <string,struct node> N;
void BFS(){
int i,tx,ty,nowx,nowy,lenc;
string c,d;
for (i=0;i<4;i++)//第一步的初始化
{
tx = 1+dir[i][0];
ty = 1+dir[i][1];
if (tx>=1 && ty>=1 && ty<=column && tx<=r && a[tx][ty]==0)//能走
{
c = D[i];
q.push(c);
N[c].x = tx;
N[c].y = ty;
N[c].steps = 1;
}
}
while (!q.empty()){
c = q.front();
nowx = N[c].x;
nowy = N[c].y;
q.pop();
for (i=0;i<4;i++)//四个方向
{
d = c;
lenc = c.size();
tx = nowx+dir[i][0];
ty = nowy+dir[i][1];
if (tx>0 && tx<=r && ty>0 && ty<=column && a[tx][ty]==0)//能走
{
d+=D[i];//加上新路径
if (tx==r && ty==column)//第一个到终点的
{
printf("%d\n",N[c].steps+1);
cout << d;
F = 1;
break;
}
else if (N[d].x==0)//未出现过
{
q.push(d);
N[d].x = tx;
N[d].y = ty;
N[d].steps = N[c].steps+1;
}
}
}
if (F==1)
break;
}
}
int main()
{
int i,j;
char temp;
scanf("%d %d\n",&r,&column);//读入行列数
for (i=1;i<=r;i++)
{
for (j=1;j<=column;j++)
{
scanf("%c",&temp);
a[i][j] = temp-48;
}
getchar();//读入换行符
}
BFS();
return 0;
}
分析与修改:这里的map是为了保证每一种路径当前所在的坐标位置、步数和内容相对应而设定的,而这一功能也可以存入queue中,这样可以节约map查找所带来的时间;另外,我也没有保证不走回头路,因此,需要设置另一个二维数组,减少走回头路所带来的时间消耗。