老鼠走迷宫问题(2016百度机试题)
2016-09-16 17:13
267 查看
问题描述:
在迷宫某处放一大块奶酪,把一只老鼠Mooshak放入迷宫。
迷宫以二维数组表示,0表示墙,1表示Mooshak可以移动的路径,9表示奶酪所在位置。Mooshak从迷宫左上角(0,0)开始移动。
编写一个MazePath类的方法isPath,判断Mooshak是否能到达奶酪所在地。如果Mooshak和奶酪之间存在一条路径,isPath方法返回true,否则返回false,Mooshak不能离开迷宫或翻墙。
Mooshak可得到奶酪的8*8迷宫示例如下:
解法一: 利用回溯法求解。
回溯法又称作”试探法“,是一种系统地搜索问题的解的算法。回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。
将回溯法应用到上述待解决的问题,就是让我们贪吃的小老鼠从起点位置开始,每次选取一个可前进的方向往前走,到达新的位置后,检查是否可以继续前进:如果已经走进死胡同,原路返回前一位置尝试其他方向;否则,就从新位置继续前进。如此循环进行,直到找到奶酪为止。
代码实现涉及的2个细节:
1.需要对所有已经过的位置做上标记(标为 -1) ,防止回溯试探过程走进之前走过的位置,导致程序陷入死循环;
2.维护一个stack,每当走进新位置就将此位置信息压栈,如果陷入死胡同就弹栈,获取前一位置信息,实现原路返回的效果。如果所有可能的路径都尝试完(栈被弹空)仍未找到奶酪,则说明起点和奶酪之间不存在路径。
下面是代码实现:
解法二:利用并查集(union-find)算法判断起点和奶酪之间的连通性。
我们认为迷宫中互不相交的连通路径各自成为一个联结体,组成联结体的每个位置是相互连通的。如下图中所示,迷宫中连通路径组成了2个联结体(分别被标为红色、蓝色)。
算法首先对迷宫中每个位置进行遍历,对各位置的连通性进行初始化,也就是建立各联结体的连通信息。
(关于并查集算法详见:http://blog.csdn.net/dm_vincent/article/details/7655764)。
然后,判断起点和奶酪位置之间是否连通即可,若连通则说明老鼠可以吃到奶酪。
实现代码:
在迷宫某处放一大块奶酪,把一只老鼠Mooshak放入迷宫。
迷宫以二维数组表示,0表示墙,1表示Mooshak可以移动的路径,9表示奶酪所在位置。Mooshak从迷宫左上角(0,0)开始移动。
编写一个MazePath类的方法isPath,判断Mooshak是否能到达奶酪所在地。如果Mooshak和奶酪之间存在一条路径,isPath方法返回true,否则返回false,Mooshak不能离开迷宫或翻墙。
Mooshak可得到奶酪的8*8迷宫示例如下:
解法一: 利用回溯法求解。
回溯法又称作”试探法“,是一种系统地搜索问题的解的算法。回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。
将回溯法应用到上述待解决的问题,就是让我们贪吃的小老鼠从起点位置开始,每次选取一个可前进的方向往前走,到达新的位置后,检查是否可以继续前进:如果已经走进死胡同,原路返回前一位置尝试其他方向;否则,就从新位置继续前进。如此循环进行,直到找到奶酪为止。
代码实现涉及的2个细节:
1.需要对所有已经过的位置做上标记(标为 -1) ,防止回溯试探过程走进之前走过的位置,导致程序陷入死循环;
2.维护一个stack,每当走进新位置就将此位置信息压栈,如果陷入死胡同就弹栈,获取前一位置信息,实现原路返回的效果。如果所有可能的路径都尝试完(栈被弹空)仍未找到奶酪,则说明起点和奶酪之间不存在路径。
下面是代码实现:
import java.util.Stack; public class MazePath { private static final int PASSED = -1; // 已经过标识 private static final int WALL = 0; // 墙 private static final int PATH = 1; // 可移动的路径 private static final int CHEESE = 9; // 奶酪 private static final int STEP_FORWARD = 1; // 向前一步 private static final int STEP_BACK = -1; // 向后一步 private int[][] grid; // 迷宫 private final int x_len; // 迷宫行数 private final int y_len; // 迷宫列数 private int x; // 当前位置纵坐标 private int y; // 当前位置横坐标 public MazePath(int[][] grid) { this.grid = grid; x_len = grid.length; y_len = grid[0].length; } public boolean isPath() { if (grid == null || grid[x][y] == WALL) // 处理异常输入 return false; Stack<Point> stack = new Stack<Point>(); // 栈,用于记录已经过的位置 stack.push(new Point(x, y)); // 栈被弹空,则说明所有路径都已被尝试过 while (!stack.isEmpty()) { // 判断当前位置是否有奶酪,有则返回true,无则将当前位置标记为已经过 if (grid[x][y] == CHEESE) return true; else grid[x][y] = PASSED; // 可以向左走一步 if (y > 0 && grid[x][y - 1] != PASSED && grid[x][y - 1] != WALL) { turn_left(); stack.push(new Point(x, y)); continue; } // 可向右走一步 if (y < y_len-1 && grid[x][y + 1] != PASSED && grid[x][y + 1] != WALL) { turn_right(); stack.push(new Point(x, y)); continue; } // 可向上走一步 if (x > 0 && grid[x - 1][y] != PASSED && grid[x - 1][y] != WALL) { turn_up(); stack.push(new Point(x, y)); continue; } // 可向下走一步 if (x < x_len-1 && grid[x + 1][y] != PASSED && grid[x + 1][y] != WALL) { turn_down(); stack.push(new Point(x, y)); continue; } // 无可前进方向,返回上一位置 Point pos = stack.pop(); x = pos.i; y = pos.j; } return false; // 所有路径已探索完,未找到奶酪 } private void turn_left() { if (y <= 0) { throw new IllegalArgumentException("Go left when y =" + y); } y = y + STEP_BACK; } private void turn_right() { if (y >= y_len - 1) { throw new IllegalArgumentException("Go right when y =" + y); } y = y + STEP_FORWARD; } private void turn_up() { if (x <= 0) { throw new IllegalArgumentException("Go up when x =" + x); } x = x + STEP_BACK; } private void turn_down() { if (x >= x_len - 1) { throw new IllegalArgumentException("Go down when x =" + x); } x = x + STEP_FORWARD; } // 内部类:已经过的位置 class Point { private int i; private int j; public Point(int i, int j) { this.i = i; this.j = j; } public String toString() { return "(" + i +", " + j +")"; } } // 测试代码 public static void main(String[] args) { int[][] grid = new int[][] {{1, 1, 1, 1, 1, 0, 0, 1}, {1, 0, 0, 0, 1, 1, 1, 1}, {1, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 1, 0, 0, 0, 0, 9}, {1, 1, 1, 0, 1, 1, 0, 1}, {1, 0, 1, 0, 0, 1, 0, 1}, {1, 0, 0, 0, 0, 1, 0, 1}, {1, 1, 1, 1, 1, 1, 1, 1}}; MazePath obj = new MazePath(grid); System.out.println(obj.isPath()); } }
解法二:利用并查集(union-find)算法判断起点和奶酪之间的连通性。
我们认为迷宫中互不相交的连通路径各自成为一个联结体,组成联结体的每个位置是相互连通的。如下图中所示,迷宫中连通路径组成了2个联结体(分别被标为红色、蓝色)。
算法首先对迷宫中每个位置进行遍历,对各位置的连通性进行初始化,也就是建立各联结体的连通信息。
(关于并查集算法详见:http://blog.csdn.net/dm_vincent/article/details/7655764)。
然后,判断起点和奶酪位置之间是否连通即可,若连通则说明老鼠可以吃到奶酪。
实现代码:
import edu.princeton.cs.algs4.WeightedQuickUnionUF; public class MazePath2 { private static final int WALL = 0; // 墙 private static final int PATH = 1; // 可移动的路径 private static final int CHEESE = 9; // 奶酪 private int[][] grid; // 迷宫 private final int x_len; // 迷宫行数 private final int y_len; // 迷宫列数 private int x; // 奶酪位置纵坐标 private int y; // 奶酪位置横坐标 public MazePath2(int[][] grid) { this.grid = grid; x_len = grid.length; y_len = grid[0].length; x = Integer.MIN_VALUE; y = Integer.MAX_VALUE; } public boolean isPath() { if (grid == null || grid[0][0] == WALL) // 处理异常输入 return false; // 并查集数据结构 WeightedQuickUnionUF uf = new WeightedQuickUnionUF(x_len * y_len); // 初始化各位置连通性 for (int i = 0; i < x_len; i++) { for (int j = 0; j < y_len; j++) { if (grid[i][j] != WALL) { // 找到奶酪所在位置 if (grid[i][j] == CHEESE) { x = i; y = j; } if (i > 0 && grid[i - 1][j] == PATH) { uf.union(transferIndex(i, j), transferIndex(i-1, j)); } if (i < x_len-1 && grid[i + 1][j] == PATH) { uf.union(transferIndex(i, j), transferIndex(i+1, j)); } if (j > 0 && grid[i][j - 1] == PATH) { uf.union(transferIndex(i, j), transferIndex(i, j-1)); } if (j < y_len-1 && grid[i][j + 1] == PATH) { uf.union(transferIndex(i, j), transferIndex(i, j+1)); } } } } if ((x | y) < 0) return false; // 迷宫内无奶酪 // 判断起点和奶酪位置的连通性,并返回 return uf.connected(transferIndex(0, 0), transferIndex(x, y)); } // 将二维数组索引转换成一维数组索引 private int transferIndex(int i, int j) { return i * y_len + j; } public static void main(String[] args) { int[][] grid = new int[][] {{1, 0, 9, 1, 1, 0, 0, 1}, {0, 0, 0, 0, 1, 1, 1, 1}, {1, 0, 0, 0, 0, 0, 0, 1}, {1, 0, 1, 0, 0, 0, 0, 0}, {1, 1, 0, 0, 1, 0, 1, 1}, {1, 0, 1, 0, 0, 1, 0, 1}, {1, 0, 0, 0, 0, 1, 0, 1}, {1, 1, 1, 1, 1, 1, 1, 1}}; MazePath2 obj = new MazePath2(grid); System.out.println(obj.isPath()); } }
相关文章推荐
- 罪犯转移问题思考——2016百度编程题
- 百度2016实习 前端试题中的编程题2:Excel地址的相互转换 [2015南桥杯试题]
- 百度2016实习 前端试题中的编程题1:输出乘法表中第k小的数的值
- 百度2016暑假实习机试题(部分)
- 百度面试题一道2(著名的蚂蚁问题)
- 试题 2、飞机加油问题
- 本博客在百度消失问题近况汇报
- 百度的问题、梁冬的架空、公关的素质
- 试题 2、飞机加油问题
- 百度C 语言吧 · 问题资料大全
- 百度试题总结
- 彩票问题求解(赣州市NOIP2006选拔赛试题)
- 在servlet设置javabean(最近学习j2ee遇到这个问题,在百度上找到的很好的回答)
- 关于类似百度错别字关键字搜索的问题
- 一个经典Delphi算法(老鼠走迷宫问题)
- php试题: 四人过桥问题
- 百度(Baidu.com) [C 语言吧] · 问题资料大全
- 试题 1、五边形问题
- 百度“水果开会”问题探讨
- 百度收录论坛的问题