【分析】
首先用一个二维数组存储该迷宫,从起点位置(0,0)开始分支遍历,将起点位置作为当前扩展结点,扩展出所有出未被访问过的有效结点,即该位置不是路障,而是通路,将所有这些有效结点依次加入到队列中,同时记录该有效结点的前驱结点,可用二维数组进行记录,此时从起始位置到达该有效结点的位置的路长为到达前驱结点的路长加1。再从队列中取出一个结点作为当前的扩展结点,重复上述操作,直到队列为空或者达到终点位置。
当没有通路时,输出“No”;
当存在通路时,输出“Yes”,并输出最短路径,同时输出最短路径的路长。
【伪代码】
void sovle(int n, int m, int[][] g) {
int[] dx 定义x方向上的移动方式;
int[] dy 定义y方向上的移动方式;
定义活结点队列 q;
定义用于存储位置是否被访问的二维数组;
将起始位置加入到队列q中;
标记起始位置已被访问;
定义用于记录前驱结点的二维数组;
while(队列q不为空) {
从队列q中取出队头作为当前的扩展结点;
if(该扩展结点为终点位置)
break;
for(int i = 0; i < 4; i++) {
通过移动,得出新的子结点;
if(该子结点没有越界,并且该结点未被访问,为通路) {
将该子结点加入到队列中;
标记该子结点位置已被访问;
记录该子结点的前驱结点为当前扩展结点;
记录起始位置到达该结点的路长 = 前驱结点的路长 + 1;
}
}
}
if(终点位置的结点没有被访问过)
输出No;
else {
创建一个栈,并将终点结点加入栈中;
while(true) {
找到当前结点的前驱结点,加入到栈中;
if(前驱结点的位置为起始位置)
break;
修改当前结点为前驱结点;
}
从栈中依次取出结点,输出结点位置,即为所求最短通道路径;
输出最短路径长度;
}
}
【程序】
用java语言编写程序,代码如下:
import java.io.BufferedInputStream;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import java.util.Stack;
public class MazeGame {
public static void main(String[] args) {
Scanner input = new Scanner(new BufferedInputStream(System.in));
int n = 0, m = 0;
while(input.hasNext()) {
n = input.nextInt();
m = input.nextInt();
if(n == 0 && m == 0)
break;
int[][] g = new int[n][m];
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
g[i][j] = input.nextInt();
solve(n, m, g);
}
}
public static void solve(int n, int m, int[][] g) {
int[] dx = {1, 0, -1, 0};
int[] dy = {0, 1, 0, -1};
Queue<Pos> q = new LinkedList<Pos>();
boolean[][] isVisited = new boolean[n][m];
Pos p0 = new Pos(0, 0);
p0.dist = 0;
isVisited[0][0] = true;
q.add(p0);
Pos[][] prev = new Pos[n][m];
int x = 0, y = 0, tx = 0, ty = 0;
Pos p = new Pos();
while(!q.isEmpty()) {
p = q.poll();
x = p.x; y = p.y;
if(x == n - 1 && y == m - 1) break;
for(int i = 0; i < 4; i++) {
tx = dx[i] + x; ty = dy[i] + y;
if(tx >= 0 && ty >= 0 && tx < n && ty < m) {
if(!isVisited[tx][ty] && g[tx][ty] == 0) {
Pos p1 = new Pos(tx, ty);
prev[tx][ty] = p;
p1.dist = p.dist + 1;
isVisited[tx][ty] = true;
q.add(p1);
}
}
}
}
if(!isVisited[n - 1][m - 1])
System.out.println("No");
else {
System.out.println("Yes");
Stack<Pos> sp = new Stack<Pos>();
sp.add(p);
int ex = n - 1, ey = m - 1;
while(true) {
Pos p2 = prev[ex][ey];
sp.add(p2);
if(p2.x == 0 && p2.y == 0)
break;
ex = p2.x;
ey = p2.y;
}
Pos p3 = sp.pop();
System.out.print("(" + p3.x + "," + p3.y + ")");
int i = 2;
while(!sp.isEmpty()) {
p3 = sp.pop();
if(i % 10 == 1) {
System.out.print("\n(" + p3.x + "," + p3.y + ")");
}
else
System.out.print(" (" + p3.x + "," + p3.y + ")");
i++;
}
System.out.println("\n" + p.dist);
}
}
static class Pos {
int x, y;
int dist;
public Pos(int x, int y) {
this.x = x;
this.y = y;
}
public Pos() {
}
}
}
【结果】
运行程序,输入10 10和迷宫的布局,输出结果:
【体会】
分支限界法以广度优先或以最小耗费优先的方式搜索解空间。分支限界法的搜索策略是,在扩展结点处,先生成其所有的儿子结点(分支),然后再从当前的活结点表中选择下一个扩展结点。为了有效地选择下一扩展结点,加速搜索的进程,在每一活结点处,计算一个函数值(限界),并根据函数值,从当前活结点表中选择一个最有利的结点作为扩展结点,使搜索朝着解空间上有最优解的分支推进,以便尽快地找出一个最优解。