二维迷宫问题

去哪网的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;
	}
}
本文纯属原创,如有错误,请及时指出,感激不尽,谢谢!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值