去哪网的0、1的迷宫问题
本来患有懒癌的我准备歇息一天,没想到一个难得我流汗的问题,不得不发上来与大家分享。
上午一同学发来这么一道有意思的题,话不多说先上图,
我大天朝的人情景模拟还是很厉害的,然而我并不懂什么特斯拉,只能略微从网上简单一搜,首先映入眼帘的是、、一群我并买不起的车(说到这个我就有种抢银行的冲动),其次我才顺着自动驾驶技术查到tesla的自主研发的前端技术AutoPilot,此技术类似于飞机上使用的半自动驾驶,必须有人监督和进行临时控制,可以说并没有达到什么不需要方向盘刹车的地步(google大神的全自动我就不说了,毕竟他们现在是围棋界老大),虽然不那么智能,但至少能让驾驶员轻松一半以上。
可惜我不是造汽车的,所以言归正传,如图所示,刨去我们看不懂的,剩下的意思就是,给你一个n*n矩阵,里面存的数只包括0,1,1为墙,不能走,0是路,可以通过,找到从[0,0]到[n-1,n-1]的最短距离,并输出路径坐标,如果没有则输出nopath。
是不是刚看到的时候很简单?我也那么想的,可是,我做了2个小时,还有一定问题,直到2个半小时的时候才初探端倪,那么我们老惯例,从特殊情况说起。
[0,0]到[n-1,n-1]首先得先买门票吧,人家不让你进,你还转悠啥,趁早回去歇着,所以
if(map[0][0]==1) return "nopath";
好了,我们进来了,该走了,要说再有特殊情况就该是下面和右面都是1了,当然这也算正常情况中的一种,所以我们就不单独判断了,难题到这里就真正的出现了,刚开始我只是简单的认为把每一个坐标的上下左右的情况分析出来,看不是来过的路就可以继续走,如果撞墙了,就记录下数组长度,与走过的路,可是这样不仅需要用map记录路程的长度和路径内容,最后还需要遍历进行比较,操作相当复杂,于是我就想同样是动态规划,为什么不能随走随记录呢,而且撞墙的时候,我不需要回起点,我只需要走到第一个分岔口再走不就好了?
带着这样的想法,我创建了一个search方法,如下
void search(Stack s,int row,int col)
{
int r,c,num;
num=Arr[row][col];
r=row;c=col+1;
{
if(cango(num, r, c)){
Arr[r][c] = num-1;
s.push(new Position(r, c));
search(s, r, c);
(Position)s.pop();
}
}
r=row+1;c=col;
{
if(cango(num, r, c)){
Arr[r][c] = num-1;
s.push(new Position(r, c));
search(s, r, c);
(Position)s.pop();
}
}
r=row-1;c=col;
{
if(cango(num, r, c)){
Arr[r][c] = num-1;
s.push(new Position(r, c));
search(s, r, c);
s.pop();
}
}
r=row;c=col-1;
{
if(cango(num, r, c)){
Arr[r][c] = num-1;
s.push(new Position(r, c));
search(s, r, c);
s.pop();
}
}
}
为了理解方便我将每一部分的判断用大括号包裹起来,进行分析,先设定一个全局变量num,记录当前走到的位置所需的步数因为墙是正数,所以我们就用负数来表示步数就可以,然后我们先往右边走,可是我们怎么判断右边能不能同行呢,则需要加上判断的方法,
boolean cango(int nearly,int row,int col)
{
if(row>=0 &&col>=0 &&row<Rownum &&col<Colnum &&Arr[row][col]!=1)
if(Arr[row][col]==0) return true;
return false;
}
最开始当然要判断要走的位置是否在迷宫范围(万一外面是悬崖,可就没有重来的机会了),所以当前横纵坐标必须大于等于0,小于矩阵的长度,确定了在迷宫范围里,我们就需要判断是不是墙了,Arr[row][col]!=1;OK,不是墙那我们就过去吧。同时我们要用面向对象的思想去解决问题,所以我把每个坐标都变成一个对象点Position去调用处理。再怎么走?当然是重复刚才的过程了,这个时候你们就会问,那你弄个栈干啥,让数组累了睡觉么。no,no,当然不是,如果玩过迷宫游戏就应该知道,一般走过的路,都会有个小地图记录路径,嘿嘿,明白了吧,stack就是干这个的,而且不仅如此,还有一个妙用等会看官们你们就知道了。到此该怎么走就基本大体确定了,不过各位看官再读一下题目,是的,要求最短路径。啥,最短路径,我都跑到终点了,你让我再跑一趟,这不是玩我么(不是我玩啊,我也是受害者啊)。好吧为了让你省点力气,我就大发慈悲的告诉你个常识,走迷宫,如果我们走错了路,或者走到死胡同了,我们是退回到原点重新走一遍呢,还是走回第一个分岔口,再重新选择另外一条路。答案肯定是第二种,发现了木有,先进后出,后进先出,对,这就是栈,它的作用就体现出来了。当我们把走过的坐标点存入到栈内,遇到走不通的时候,就退回到上一个节点,然后 查看其他方向是否能通行,依此类推,可以遍历所有的坐标点。到这个时候,如果是leetcode我觉得就该爆time limit的错误了,当然也可能侥幸,你遍历的方向刚好都是最小值,也算你赢了。不过各位看官可能有疑问了,为什么现在走的时间长呢,那我就画个图说明一下吧。
如图所示,红色表示墙,蓝色线条表示一条最短路径,绿色线条表示一条我们多走的一条路线。
看到这大家应该恍然大悟了吧,是的,我们能2步就到达的地方,走了4步,生生的锻炼了一次身体(多走一次也就算了,次次绕远。别说小编了,电脑都受不了)。于是我们就要想着控制一下走过的路径,于是刚才为二维数组赋值的num属性就有了他的用处,我们只需要判断新走的点的num值减一与将要走的点的num值谁大谁小,因为是负数,所以num大的反而走的步数少,num小的反而走的步数多,我们将cango函数修改一下,代码如下:
if(row>=0 &&col>=0 &&row<Rownum &&col<Colnum &&Arr[row][col]!=1)//为越界且可走通
if(Arr[row][col]==0) return true;
else return (nearly-1)>Arr[row][col];
return false;
最后,再加上题目所需的打印函数即可,这里我直接返回的string类型,所以打印方法就不单独列出了,各位看的也累了,我这就把完整代码给大家贴上。
public class RoadCopile {
int[][] Arr;
int Rownum,Colnum;
int Beginrow,Begincol,Endrow,Endcol;
int state=0;
public String road(int[][] m) {
Arr = m;
Rownum = m.length;
Colnum = m.length;
Beginrow = 0;
Begincol = 0;
Endrow = m.length-1;
Endcol = m.length-1;
Arr[Beginrow][Begincol] = -1;
String r = "";
if(m[0][0]==1) return "nopath";
search(new Stack<>(), Beginrow, Begincol);
if(state==1){
while(!minstack.empty()){
r = r + "("+((Position)minstack.peek()).X+","+((Position)minstack.peek()).Y+")";
minstack.pop();
}
r=r+"(0,0)";
return r;
}else{
return "nopath";
}
}
boolean canplace(int nearly,int row,int col)
{
if(row>=0 &&col>=0 &&row<Rownum &&col<Colnum &&Arr[row][col]!=1)
if(Arr[row][col]==0) return true;
else return (nearly-1)>Arr[row][col];
return false;
}
Stack minstack = new Stack<>();
void search(Stack s,int row,int col)
{
if(row==Endrow && col==Endcol){
if(s.size()<minstack.size()||minstack.empty()){
state =1;
minstack = (Stack) s.clone();
}
return;
}
int r,c,num;
num=Arr[row][col];
r=row;c=col+1;
{
if(canplace(num, r, c)){
Arr[r][c] = num-1;
s.push(new Position(r, c));
search(s, r, c);
Position p = (Position)s.pop();
}
}
r=row+1;c=col;
{
if(canplace(num, r, c)){
Arr[r][c] = num-1;
s.push(new Position(r, c));
search(s, r, c);
Position p = (Position)s.pop();
}
}
r=row-1;c=col;
{
if(canplace(num, r, c)){
Arr[r][c] = num-1;
s.push(new Position(r, c));
search(s, r, c);
Position p = (Position)s.pop();
}
}
r=row;c=col-1;
{
if(canplace(num, r, c)){
Arr[r][c] = num-1;
s.push(new Position(r, c));
search(s, r, c);
Position p = (Position)s.pop();
}
}
}
}
class Position{
int X,Y;
Position(){}
Position(int x, int y){
this.X = x;
this.Y = y;
}
}
本文纯属原创,如有错误,请及时指出,感激不尽,谢谢!!