Java 图:找到一个算法来确定矩形迷宫中从一点到另一点的最短路径?
在迷宫中,我非常头痛,试图制定一个合适的算法,从开始位置转到退出位置。 值得一提的是,迷宫是矩形的,最大尺寸500x500,从理论上讲,DFS可以通过一些分支和绑定技术来解决Java 图:找到一个算法来确定矩形迷宫中从一点到另一点的最短路径?,java,algorithm,graph,Java,Algorithm,Graph,在迷宫中,我非常头痛,试图制定一个合适的算法,从开始位置转到退出位置。 值得一提的是,迷宫是矩形的,最大尺寸500x500,从理论上讲,DFS可以通过一些分支和绑定技术来解决 10 3 4 7 6 3 3 1 2 2 1 0 2 2 2 4 2 2 5 2 2 1 3 0 2 2 2 2 1 3 3 4 2 3 4 4 3 1 1 3 1 2 2 4 2 2 1 Output: 5 1 4 2
10 3 4
7 6
3 3 1 2 2 1 0
2 2 2 4 2 2 5
2 2 1 3 0 2 2
2 2 1 3 3 4 2
3 4 4 3 1 1 3
1 2 2 4 2 2 1
Output:
5 1 4 2
说明:我们的经纪人每走一步都会释放能量,他只能上下左右移动。此外,如果代理到达时剩余能量为零或更少,他就会死亡,因此我们打印类似“不可能”的内容 因此,在输入中,10是初始代理的能量,34是开始的位置(即第3列第4行),我们有一个迷宫7x6。将其视为一个迷宫,我想在其中找到一个出口,为代理提供更好的剩余能量(最短路径) 如果存在通向相同剩余能量的路径,我们当然会选择步骤数较少的路径 我需要知道,在这些限制条件下,在最坏的情况下,将DFS连接到迷宫500x500是否可行,以及如何做到这一点,在每个步骤中存储剩余能量,以及到目前为止采取的步骤数 输出意味着代理以剩余能量=5的方式分2步到达出口位置1 4。如果我们仔细观察,在这个迷宫中,也有可能在位置31(第3列,第1行)以相同的能量退出,但有3个步骤,因此我们选择了更好的一个 记住这些,有人能帮我一些代码或伪代码吗? 我很难用2D阵列解决这个问题,以及如何存储剩余能量、路径(或所采取的步数) 编辑: 拉里,就像我说的,我对代码有点困惑。这是我到目前为止所做的尝试,只是为了用较少的步骤确定从起点到出口的最短路径,同时修复出口
public class exitFromMaze {
int energy, startY, startX, xMax, yMax;
int adjMatrix[][];
boolean visited[][];
ArrayList<Cell> neighbours;
//ArrayList<Cell> visited;
Cell start;
Stack<Cell> stack;
public exM() {
Scanner cin = new Scanner(System.in);
int nrTests = cin.nextInt();
for (int i = 0; i < nrTests; i++) {
energy = cin.nextInt();
startY = cin.nextInt()-1; //start at columnstartY
startX = cin.nextInt()-1; //start at line startX
xMax = cin.nextInt();//7 cols
yMax = cin.nextInt(); //8 rows
adjMatrix = new int[yMax][xMax];
visited = new boolean[yMax][xMax];
//visited = new ArrayList<Cell>();
this.stack = new Stack<Cell>();
for (int r = 0; r < yMax; r++) { // yMax linhas
for (int c = 0; c < xMax; c++) { // xMax colunas
adjMatrix[r][c] = cin.nextInt();
visited[r][c] = false;
//System.out.println("matrix["+r+"]["+c+"] = "+adjMatrix[r][c]);
}
}
start= new Cell(startX, startY, 0);
//adiciona a pos actual à pilha de celulas/nos
stack.push(start);
//printArray(yMax, xMax);
findBestExit();
}//end_of_test_Cases
}
private void findBestExit() {
// BEGINNING OF DEPTH-FIRST SEARCH
Cell curCell;
while (!(stack.empty())) {
curCell = (Cell) (stack.pop());
//just fix an exit point ...for now (if it works for one, it has to work for all the other possible exits)
if (curCell.row==0 && curCell.col== 4) {
System.out.println("Arrived at pos: "+curCell.row+","+curCell.col+" with E= "+(energy-curCell.getEnergy())+" with "+curCell.getSteps()+" steps");
//finish = curCell;
break;
} else {
visited[curCell.row][curCell.col] = true;
}
this.neighbours = (ArrayList<Cell>) curCell.getNeighbours(this.xMax, this.yMax);
for (Cell neighbourCell: neighbours) {
//1- I think something's missing here and it would be here the point to cut some cases...isn't it?
if ( curCell.getEnergy() + neighbourCell.getEnergy() < this.energy && !visited[neighbourCell.row][neighbourCell.col]){
neighbourCell.energy+= curCell.energy;
neighbourCell.setSteps(curCell.getSteps()+1);
neighbourCell.setPrevious(curCell);
stack.push(neighbourCell);
}
// ...
}
}
// END OF DEPTH-FIRST SEARCH and DIJKSTRA?
}
class Cell {
int row;
int col;
int energy;
int steps;
Cell previous;
//Node next;
public Cell(int x, int y, int steps) {
this.row = x;
this.col = y;
this.energy = adjMatrix[x][y];
this.steps = steps;
//this.next = null;
this.previous = null;
}
public Cell(int x, int y, Cell prev) {
this.row = x;
this.col = y;
this.steps = 0;
this.energy = adjMatrix[x][y];
this.previous = prev;
}
@Override
public String toString() {
return "(,"+this.getRow()+","+this.getCol()+")";
}
public int getEnergy() {
return energy;
}
public void setEnergy(int energy) {
this.energy = energy;
}
public Cell getPrevious() {
return previous;
}
public void setPrevious(Cell previous) {
this.previous = previous;
}
public int getRow() {
return row;
}
public void setRow(int x) {
this.row = x;
}
public int getCol() {
return col;
}
public void setCol(int y) {
this.col = y;
}
public int getSteps() {
return steps;
}
public void setSteps(int steps) {
this.steps = steps;
}
public Cell south(int verticalLimit) {
Cell ret = null;
if (row < (verticalLimit - 1)) {
ret = new Cell(row+1, col, this);
//ret.previous = this;
}
return ret;
}
/**
* Gives the north to our current Cell
* @return the Cell in that direction, null if it's impossible
* to go in that direction
*/
public Cell north() {
Cell ret = null;
if (row > 0) {
ret = new Cell(row-1, col ,this);
//ret.previous = this;
}
return ret;
}
/**
* Gives the west (left) to our current Cell
* @return the Cell in that direction, null if it's
* impossible to go in that direction
*/
public Cell west() {
Cell ret = null;
if (col > 0) {
ret = new Cell(row, col-1,this);
//ret.previous = this;
}
return ret;
}
/**
* Gives the east direction(right) to our current Cell
* @return the Cell in that direction, null if it's
* impossible to go in that direction
*/
public Cell east(int horizontalLimit) {
Cell ret = null;
//if it's inside the number max of collumns
if (col < (horizontalLimit - 1)) {
ret = new Cell(row , col+1, this);
}
return ret;
}
public List getNeighbours(int xlimit, int ylimit) {
ArrayList<Cell> res = new ArrayList<Cell>(4);
Cell n;
n = south(ylimit);
if (n != null) {
res.add(n);
}
n = north();
if (n != null) {
res.add(n);
}
n = east(xlimit);
if (n != null) {
res.add(n);
}
n = west();
if (n != null) {
res.add(n);
}
return res;
}
}
private void printArray(int h, int w) {
int i, j;
// print array in rectangular form
System.out.print(" ");
for (i = 0; i < w; i++) {
System.out.print("\t" + i);
}
System.out.println();
for (int r = 0; r < h; r++) {
System.out.print(" " + r);
for (int c = 0; c < w; c++) {
System.out.print("\t" + adjMatrix[r][c]);
}
System.out.println("");
}
System.out.println();
}
public static void main(String args[]) {
new exM();
}
}
它应该打印:
12 5 1 8
i、 例如,代理以更好的出口(0,4)退出,剩余能量=12,只需8步
有了我的想法,有了你的帮助,指出我的缺点或纠正它们会有很大的帮助吗?
我真是受够了。。。因为我要把一些简单的事情复杂化
更多输入/输出(当无法实现有效退出(能量>0时),只需打印该事实)
只需使用,在基数方向上使用隐式图。使用堆实现,它将是O(V log V)
,这对于500x500
来说应该足够好了。第一次放松节点时,使用的能量最低。使用此算法,您可以非常轻松地设置节点的前置器
编辑:一些解释Dijkstra算法的伪代码:
function Dijkstra( graph, source ):
// distance is infinity to everywhere initially
dist = initialize list of size V to infinity
// no vertices pointed to anything
previous = initialize list of size V to undefined
// distance from source to source is 0
dist[source] = 0
Q = priority queue
insert all vertices into Q
while Q is not empty:
// get the vertex closest to the source
current_vertex = Q.pop
if dist[current_vertex] == infinity
break
// these are the adjacent vertices in the four cardinal direction
for each vertex next to current_vertex:
// if it costs less energy to go to vertex
// from current_vertex
if ( dist[current_vertex] + energy[vertex] < dist[vertex] )
dist[vertex] = dist[current_vertex] + energy[vertex]
previous[vertex] = current_vertex
// Another if statement here to
// minimize steps if energy is the same
// Now after this is done, you should have the cheapest cost to
// each vertex in "dist". Take the cheapest one on the edge.
// You can walk backwards on the "previous" to reconstruct the path taken.
函数Dijkstra(图,源):
//最初,到任何地方的距离都是无穷远的
dist=将大小为V的列表初始化为无穷大
//没有指向任何东西的顶点
previous=将大小为V的列表初始化为未定义
//源到源的距离为0
距离[源]=0
Q=优先级队列
将所有顶点插入Q
Q不是空的:
//获取离源最近的顶点
当前顶点=Q.pop
如果距离[当前顶点]==无穷大
打破
//这些是四个基数方向上的相邻顶点
对于当前_顶点旁边的每个顶点:
//如果到顶点花费的能量更少
//从当前顶点开始
if(距离[当前顶点]+能量[顶点]<距离[顶点])
距离[顶点]=距离[当前顶点]+能量[顶点]
上一个[顶点]=当前_顶点
//这里的另一个if语句
//如果能量相同,则最小化步骤
//现在,在完成这项工作之后,您应该有最便宜的成本
//“距离”中的每个顶点。拿边上最便宜的。
//您可以在“上一步”上向后走,以重建所走的路径。
这是一般的伪代码,不过您还必须跟踪步骤的数量,主要是作为一个断开连接的步骤,因此不需要做太多的工作
至于DFS解决方案,这取决于能量的大小。如果它是有界的、小的和整数,您可以在x-y-e
上将2D图形转换为3D图形,其中e
是剩余的能量-您从初始能量开始,然后逐步向下,但要跟踪您以前去过的地方
编辑:对于DFS解决方案,它应该是
O(H*W*E)
,因为EA*是一种方式,如果您担心效率:查看它。或者如果你觉得勇敢,你可以选择D*。。这两种方法都更有效(因为它们使用启发式方法来估计距离),但实现起来并不困难
BFS显然不是实现寻路的好方法,它很简单。这里有一个实现,它将地图编码为您必须移动到出口的方向。它可以从整个地图中的任何可访问点找到最短路径:
import java.util.ArrayList;
import java.util.List;
public class Maze {
private static final char E = 'E'; // Ending position
private static final char X = 'X'; // Wall
private static final char O = ' '; // Space
private static final char L = 'L'; // Left
private static final char R = 'R'; // Right
private static final char U = 'U'; // Up
private static final char D = 'D'; // Down
private static final char FALSE = '0'; // Not accessible
private static final char TRUE = '1'; // Is accessible
private static final Node END_NODE = new Node(4, 4);
public static void main(String[] args) {
char[][] maze = new char[][] {
{X, X, X, X, X, X},
{X, O, O, X, O, X},
{X, O, X, X, O, X},
{X, O, O, O, X, X},
{X, X, O, X, O, X},
{X, O, O, O, O, X},
{X, O, X, X, O, X},
{X, X, X, X, X, X}};
// PLOT THE DESTINATION CELL AND ADD IT TO LIST
List<Node> nodes = new ArrayList<Node>();
nodes.add(END_NODE);
maze[END_NODE.row][END_NODE.col] = E;
// PRINT THE MAZE BEFORE ANY CALCULATIONS
printMaze(maze);
// SOLVE THE MAZE
fillMaze(maze, nodes);
printMaze(maze);
// CONVERT MAZE TO AN ADJACENCY MATRIX
compileMaze(maze);
printMaze(maze);
}
/**
* The parallel arrays define all four directions radiating from
* the dequeued node's location.
*
* Each node will have up to four neighboring cells; some of these
* cells are accessible, some are not.
*
* If a neighboring cell is accessible, we encode it with a directional
* code that calculates the direction we must take should we want to
* navigate to the dequeued node's location from this neighboring cell.
*
* Once encoded into our maze, this neighboring cell is itself queued
* up as a node so that recursively, we can encode the entire maze.
*/
public static final void fillMaze(char[][] maze, List<Node> nodes) {
int[] rowDirections = {-1, 1, 0, 0};
int[] colDirections = { 0, 0, -1, 1};
// dequeue our first node
Node destination = nodes.get(0);
nodes.remove(destination);
// examine all four neighboring cells for this dequeued node
for(int index = 0; index < rowDirections.length; index++) {
int rowIndex = destination.row + rowDirections[index];
int colIndex = destination.col + colDirections[index];
// if this neighboring cell is accessible, encode it and add it
// to the queue
if(maze[rowIndex][colIndex] == O) {
maze[rowIndex][colIndex] = getOppositeDirection(rowDirections[index], colDirections[index]);
nodes.add(new Node(rowIndex, colIndex));
}
}
// if our queue is not empty, call this method again recursively
// so we can fill entire maze with directional codes
if(nodes.size() > 0) {
fillMaze(maze, nodes);
}
}
/**
* Converts the maze to an adjacency matrix.
*/
private static void compileMaze(char[][] maze) {
for(int r = 0; r < maze.length; r++) {
for(int c = 0; c < maze[0].length; c++) {
if(maze[r][c] == X || maze[r][c] == O) {
maze[r][c] = FALSE;
}
else {
maze[r][c] = TRUE;
}
}
}
}
/**
* prints the specified two dimensional array
*/
private static final void printMaze(char[][] maze) {
System.out.println("====================================");
for(int r = 0; r < maze.length; r++) {
for(int c = 0; c < maze[0].length; c++) {
System.out.print(maze[r][c] + " ");
}
System.out.print("\n");
}
System.out.println("====================================");
}
/**
* Simply returns the opposite direction from those specified
* by our parallel direction arrays in method fillMaze.
*
* coordinate 1, 1 is the center of the char[][] array and
* applying the specified row and col offsets, we return the
* correct code (opposite direction)
*/
private static char getOppositeDirection(int row, int col) {
char[][] opposites = new char[][] {{O, D, O},{R, O, L},{O, U, O}};
return opposites[1 + row][1 + col];
}
}
class Node {
int row;
int col;
public Node(int rowIndex, int colIndex) {
row = rowIndex;
col = colIndex;
}
}
我不知道在哪里限制,等等。我只需要一点推动,我的问题总是把想法编码,特别是当我尝试了很多的时候。不管怎样!我将使用一些示例伪代码重新编辑,但这假设您已经熟悉Dijkstra的算法,至少在某种程度上是这样。查看维基百科链接。是的,我是,简单的迪杰斯特拉,弗洛伊德·沃沙尔,DFS和BFS。。。然后我会研究贝尔曼福特,二部匹配,最小割最大流。我只想能够做/看看,至少,如何用每种算法实现这些想法。请原谅我的英语,但现在是凌晨4:28…不能怪我!ehehehbad habbits:$能量,事实上,它是一个int。限制条件是:0
function Dijkstra( graph, source ):
// distance is infinity to everywhere initially
dist = initialize list of size V to infinity
// no vertices pointed to anything
previous = initialize list of size V to undefined
// distance from source to source is 0
dist[source] = 0
Q = priority queue
insert all vertices into Q
while Q is not empty:
// get the vertex closest to the source
current_vertex = Q.pop
if dist[current_vertex] == infinity
break
// these are the adjacent vertices in the four cardinal direction
for each vertex next to current_vertex:
// if it costs less energy to go to vertex
// from current_vertex
if ( dist[current_vertex] + energy[vertex] < dist[vertex] )
dist[vertex] = dist[current_vertex] + energy[vertex]
previous[vertex] = current_vertex
// Another if statement here to
// minimize steps if energy is the same
// Now after this is done, you should have the cheapest cost to
// each vertex in "dist". Take the cheapest one on the edge.
// You can walk backwards on the "previous" to reconstruct the path taken.
import java.util.ArrayList;
import java.util.List;
public class Maze {
private static final char E = 'E'; // Ending position
private static final char X = 'X'; // Wall
private static final char O = ' '; // Space
private static final char L = 'L'; // Left
private static final char R = 'R'; // Right
private static final char U = 'U'; // Up
private static final char D = 'D'; // Down
private static final char FALSE = '0'; // Not accessible
private static final char TRUE = '1'; // Is accessible
private static final Node END_NODE = new Node(4, 4);
public static void main(String[] args) {
char[][] maze = new char[][] {
{X, X, X, X, X, X},
{X, O, O, X, O, X},
{X, O, X, X, O, X},
{X, O, O, O, X, X},
{X, X, O, X, O, X},
{X, O, O, O, O, X},
{X, O, X, X, O, X},
{X, X, X, X, X, X}};
// PLOT THE DESTINATION CELL AND ADD IT TO LIST
List<Node> nodes = new ArrayList<Node>();
nodes.add(END_NODE);
maze[END_NODE.row][END_NODE.col] = E;
// PRINT THE MAZE BEFORE ANY CALCULATIONS
printMaze(maze);
// SOLVE THE MAZE
fillMaze(maze, nodes);
printMaze(maze);
// CONVERT MAZE TO AN ADJACENCY MATRIX
compileMaze(maze);
printMaze(maze);
}
/**
* The parallel arrays define all four directions radiating from
* the dequeued node's location.
*
* Each node will have up to four neighboring cells; some of these
* cells are accessible, some are not.
*
* If a neighboring cell is accessible, we encode it with a directional
* code that calculates the direction we must take should we want to
* navigate to the dequeued node's location from this neighboring cell.
*
* Once encoded into our maze, this neighboring cell is itself queued
* up as a node so that recursively, we can encode the entire maze.
*/
public static final void fillMaze(char[][] maze, List<Node> nodes) {
int[] rowDirections = {-1, 1, 0, 0};
int[] colDirections = { 0, 0, -1, 1};
// dequeue our first node
Node destination = nodes.get(0);
nodes.remove(destination);
// examine all four neighboring cells for this dequeued node
for(int index = 0; index < rowDirections.length; index++) {
int rowIndex = destination.row + rowDirections[index];
int colIndex = destination.col + colDirections[index];
// if this neighboring cell is accessible, encode it and add it
// to the queue
if(maze[rowIndex][colIndex] == O) {
maze[rowIndex][colIndex] = getOppositeDirection(rowDirections[index], colDirections[index]);
nodes.add(new Node(rowIndex, colIndex));
}
}
// if our queue is not empty, call this method again recursively
// so we can fill entire maze with directional codes
if(nodes.size() > 0) {
fillMaze(maze, nodes);
}
}
/**
* Converts the maze to an adjacency matrix.
*/
private static void compileMaze(char[][] maze) {
for(int r = 0; r < maze.length; r++) {
for(int c = 0; c < maze[0].length; c++) {
if(maze[r][c] == X || maze[r][c] == O) {
maze[r][c] = FALSE;
}
else {
maze[r][c] = TRUE;
}
}
}
}
/**
* prints the specified two dimensional array
*/
private static final void printMaze(char[][] maze) {
System.out.println("====================================");
for(int r = 0; r < maze.length; r++) {
for(int c = 0; c < maze[0].length; c++) {
System.out.print(maze[r][c] + " ");
}
System.out.print("\n");
}
System.out.println("====================================");
}
/**
* Simply returns the opposite direction from those specified
* by our parallel direction arrays in method fillMaze.
*
* coordinate 1, 1 is the center of the char[][] array and
* applying the specified row and col offsets, we return the
* correct code (opposite direction)
*/
private static char getOppositeDirection(int row, int col) {
char[][] opposites = new char[][] {{O, D, O},{R, O, L},{O, U, O}};
return opposites[1 + row][1 + col];
}
}
class Node {
int row;
int col;
public Node(int rowIndex, int colIndex) {
row = rowIndex;
col = colIndex;
}
}
====================================
X X X X X X
X X X
X X X X
X X X
X X X E X
X X
X X X X
X X X X X X
====================================
====================================
X X X X X X
X D L X X
X D X X X
X R D L X X
X X D X E X
X R R R U X
X U X X U X
X X X X X X
====================================
====================================
0 0 0 0 0 0
0 1 1 0 0 0
0 1 0 0 0 0
0 1 1 1 0 0
0 0 1 0 1 0
0 1 1 1 1 0
0 1 0 0 1 0
0 0 0 0 0 0
====================================