java拯救公主_广度优先搜索解决“营救公主”问题

在之前的一篇文章(迷宫营救公主算法)中提供了一个半成品的解决方案,之所以说他是半成品,是因为首先选择的算法就不对,采用的是深度优先搜索,其次也没有真正的用对深度优先算法,走过的点应该标记为已经走过,而不应该重复遍历该节点。下面的文章对于广度优先和深度优先两种算法的解释非常到位。今天准备把这个问题再完整的用正确的算法解答一遍。

就营救公主的问题而言,这里不再重复描述问题了,请点击这里查看题目分析:链接。这次选用广度优先算法来解决该问题。上面的算法介绍文章中对该算法已经分析得非常清晰了。先给解决本问题的算法伪代码:

这里每个节点有三种状态:

*)该状态表示墙,如果访问到该类型节点,则不用继续遍历。应退回至上层节点。

.)该状态表示该节点通行,访问到该节点应该将节点压入队列。并将该节点标记为已经访问。为区别图中已经有的几种元素,这里定义一个新的标记’#’来表示已经访问过的节点。同时可以使用一个二维数组记录从起始节点到该节点的访问距离step[i][j],该值应该是由其上一个节点的值加1之后得到。这样起始节点的值为1,依次每一个节点的距离都可以得到。

P)该状态表示已经搜索到了公主的位置,搜索结束。这时给出P点的距离,如果该距离值在给定的范围内,则可以营救出公主,否则失败。

解决问题的关键在于用一个step数组记录每个节点的距离起始点的距离,另外用一个’#’字符标记该节点已经被访问过,避免走“回头路”。

BFS(G, s)

init V[G] // 由测试用例实现。

status[s] = S // s王子的位置,通过一次遍历可以获取其坐标。

int count = -1;

queue q

q.add(s) // 将王子的位置入队。

while !q.isEmpty()

t = q.poll()

for each vertex v in Adj[t] // 与t邻接的点

if status[v] = 'P' // 只对未访问的操作

count = step[v] = step[t] + 1

q.clear()

break

elseif status[v] = '.'

status[v] = '#' // 标记为已经走过该节点

q.add(v)

step[v] = step[t] + 1 // 累加得到当前节点的步长

elseif status[v] = '*'

step[v] = -1 // 遇到墙,则将该节点的距离置为-1,在遍历完整个图时,

// 如果遇到最后一个节点的值为-1,则表明没有找到。

else

error

if count != -1

if count < L

return true

return false

有了上面的伪代码,翻译成代码也就非常简单了。

import java.util.LinkedList;

import java.util.List;

import java.util.Queue;

/**

* 公主被魔王抓走了,王子需要拯救出美丽的公主。他进入了魔王的城堡,魔王的城堡是一座很大的迷宫。

* 为了使问题简单化,我们假设这个迷宫是一个N*M的二维方格。迷宫里有一些墙,王子不能通过。王子只

* 能移动到相邻(上下左右四个方向)的方格内,并且一秒只能移动一步。地图由'S','P','.','*'

* 四种符号构成,'.'表示王子可以通过,'*'表示墙,王子不能通过;'S'表示王子的位置;'P'表示公主

* 的位置;T表示公主存活的剩余时间,王子必须在T秒内到达公主的位置,才能救活公主。如下图所示:

*/

public class SavePrincess

{

private int m;

private int n;

private char[][] visited;

private Position s = null;

private Position p = null;

public static void main(String[] args)

{

char maze[][] = {

/* 0 1 2 3 4 5 6 7 8 9 */

/* 0 */{ '.', '*', '.', '.', '.', '*', '.', '.', '.', '.' },

/* 1 */{ '*', 'S', '*', '.', '.', '.', '.', '.', '.', '.' },

/* 2 */{ '.', '*', '*', '.', '.', '.', '.', '.', '.', '.' },

/* 3 */{ '.', '.', '*', '*', '*', '.', '.', '.', '.', '.' },

/* 4 */{ '.', '.', '.', '.', '.', '.', '.', '.', '.', '.' },

/* 5 */{ '.', '.', '.', '.', '*', '.', '.', '.', '.', '.' },

/* 6 */{ '.', '.', '.', '.', '*', '.', '.', '.', '.', '.' },

/* 7 */{ '.', '.', '.', '.', '*', '.', '.', '.', '.', '.' },

/* 8 */{ '.', '.', '.', '.', '*', '.', '.', '.', '.', '.' },

/* 9 */{ '.', 'P', '.', '.', '*', '.', '.', '.', '.', '.' } };

new SavePrincess().saved(maze, 10, 8, 11);

}

/**

* @param vis

* M行N列迷宫

* @param m

* M

* @param n

* N

* @param t

* T

*

* @return boolean true表示营救成功,反之表示失败。

*/

public boolean saved(char[][] vis, int m, int n, int t)

{

if (t < 0)

{

return false;

}

this.m = m;

this.n = n;

this.visited = vis;

int step[][] = new int[m][n];

init(m, n, step);

if (s == null || p == null)

{

System.out.println("input visited error!");

return false;

}

Queue queue = new LinkedList();

queue.add(s);

int l = -1;

while (!queue.isEmpty())

{

Position e = queue.poll();

l = dealOneNode(step, queue, e);

}

if (l != -1 && l <= t)

{

System.out.println("Saved the princess in " + l + " seconds!");

return true;

}

System.out.println("failed saved the princess! " + l + " > " + "t");

return false;

}

private void init(int m, int n, int[][] step)

{

for (int i = 0; i != m; i++)

{

for (int j = 0; j != n; j++)

{

System.out.print(visited[i][j]);

System.out.print(' ');

step[i][j] = 0;

switch (visited[i][j])

{

case '*':

break;

case 'S':

this.s = new Position(i, j, 'S');

break;

case '.':

break;

case 'P':

this.p = new Position(i, j, 'P');

break;

default:

System.out.println("input visited error!");

System.exit(-1);

}

}

System.out.println("");

}

}

private int dealOneNode(int[][] step, Queue queue, Position e)

{

/**

* 在求相邻节点时做了一点优化,遇到墙的节点则不放到相邻节点中。

* 从设计思想和可读性上看不是太好,从运行效率考虑,可以。

*/

for (Position c : getNextPostions(e))

{

switch (c.getV())

{

case 'P':

queue.clear();

return step[e.getI()][e.getJ()] + 1;

case '.':

visited[c.getI()][c.getJ()] = '#';

queue.add(c);

step[c.getI()][c.getJ()] = step[e.getI()][e.getJ()] + 1;

break;

default:

// 不可能进入该分支。

System.err.println("some error!");

break;

}

}

return -1;

}

/**

* 取有效节点。

* 遇到墙则不加入相邻节点中。

*

* @param e

* @return

*/

private List getNextPostions(Position e)

{

int i = e.getI();

int j = e.getJ();

List lst1 = new LinkedList();

addOneNode(i + 1, j, lst1);

addOneNode(i, j + 1, lst1);

addOneNode(i - 1, j, lst1);

addOneNode(i, j - 1, lst1);

return lst1;

}

private void addOneNode(int i, int j, List lst)

{

if (i >= m || i < 0 || j >= n || j < 0)

{

return;

}

char v = visited[i][j];

switch (v)

{

case '.':

case 'P':

lst.add(new Position(i, j, v));

break;

default:

break;

}

}

private class Position

{

private int i;

private int j;

private char v;

public Position(int i, int j, char value)

{

this.i = i;

this.j = j;

this.v = value;

}

public char getV()

{

return v;

}

public int getI()

{

return i;

}

public int getJ()

{

return j;

}

public String toString()

{

return "(" + i + ", " + j + ")";

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值