我知道有很多算法可用于计算图形或网格中两点之间的最短路径,如广度优先,全对(Floyd's),Dijkstra's。
然而,正如我所注意到的,所有这些算法都会计算该图或网格中的所有路径,而不仅仅是我们感兴趣的两点之间的路径。
我的问题是: 如果我有一个网格,即一个二维数组,我有兴趣计算两点之间的最短路径,比如P1和P2,如果我可以在网格上移动的方式有限制(例如只对角线,或只对角和向上等), 什么算法可以计算出来?
请注意,如果您有答案,我希望您发布算法的名称而不是算法本身(当然,如果您也发布算法,则更好);例如,无论是Dijkstra算法,还是Floyd,或其他什么。
请帮助我,我几个月来一直在考虑这个问题!
#include<iostream>
#include <cmath>
using namespace std;
inline int Calc(int x,int y)
{
if(abs(x)>=abs(y)) return abs(x);
int z=(abs(x)+abs(y))/2;
return z+abs(abs(x)-z);
}
class SliverDistance
{
public:
int minSteps(int x1,int y1, int x2, int y2)
{
int ret=0;
if(((x1+y1)&1)!=((x2+y2)&1))y1++,ret++;
return ret+Calc(x2-x1,y2-y1);
}
};
答案 0 :(得分:38)
Lee的算法:http://en.wikipedia.org/wiki/Lee_algorithm
这实际上是一个BF搜索,这是一个例子:http://www.oop.rwth-aachen.de/documents/oop-2007/sss-oop-2007.pdf
要有效地实施它,请在此处查看我的答案:Change FloodFill-Algorithm to get Voronoi Territory for two data points? - 当我说出标记时,您用来自+ 1的位置上的数字标记它。
例如,如果你有这个网格,其中一个* =障碍,你可以向上,向下,向左和向右移动,你从S开始,必须转到D,0 =自由位置:
S 0 0 0
* * 0 *
* 0 0 *
0 0 * *
* 0 0 D
您将S放入队列中,然后“展开”它:
S 1 0 0
* * 0 *
* 0 0 *
0 0 * *
* 0 0 D
然后展开所有邻居:
S 1 2 0
* * 0 *
* 0 0 *
0 0 * *
* 0 0 D
所有这些邻居的邻居:
S 1 2 3
* * 3 *
* 0 0 *
0 0 * *
* 0 0 D
依此类推,最终你会得到:
S 1 2 3
* * 3 *
* 5 4 *
7 6 * *
* 7 8 9
因此从S到D的距离是9.运行时间是O(NM),其中N =行数,M =列数。我认为这是在网格上实现的最简单的算法,它在实践中也非常有效。它应该比传统的dijkstra更快,虽然如果用堆实现它,dijkstra可能会赢。
答案 1 :(得分:6)
使用A Star (A*)算法。
答案 2 :(得分:5)
您可能会被删除。 Dijkstra算法存在不同的变体。一个计算从每个点到每个其他点的最短路径(如Floyd的)。
但是,典型的Dijkstra算法基于优先级队列,只计算您所需的最短路径。它在执行期间确实构造了几条路径,但这些路径都是从A到可能在最终解决方案路径上的其他节点的部分路径。
因此,您可以轻松地将网格解释为图形(然后可以相应地考虑对角线等限制)并运行Dijkstra搜索从A到B的最短路径。这只是建模问题的问题,而不是你需要一些奇特的算法。
答案 3 :(得分:2)
如果您的移动足够严格(例如,您只能向右或向上移动,或向上和向右移动),那么您可以利用其重叠的子问题和次优的子结构性质并使用dynamic programming。
答案 4 :(得分:1)
我不明白的是,如果你想要A和B之间的最短路径,如果 C和D指向你,你还是不需要看A到C和A到D B'你的最短路径很可能是A-C-B或A-D-B。你只需要抛弃未连接的节点。在我的一个项目中,我获取了A点和B点,检查了其他点的连接情况,以及未删除的点数。然后我继续使用Dijkstra的算法。
答案 5 :(得分:1)
这是使用BFS从(0,0)到(0,m-1)的矩阵中的最短路径的python实现。您可以将其更改为适合变量点。
n,m,k1,k2=[int(i) for i in input().split()]
arr=[[int(j) for j in input().split()] for i in range(n)]
x=[[-1 for i in range(m)] for j in range(n)]
x[0][0]=0
vis={}
q=[(0,0)]
while len(q)!=0:
curr=q[0]
rem=q.pop(0)
vis[curr]=True
r=curr[0]
c=curr[1]
if r-1>=0 and arr[r-1][c]==0:
if vis.get((r-1,c),-1)==-1 or vis[(r-1,c)]!=True:
q.append((r-1,c))
x[r-1][c]=x[r][c]+1
if r+1<n and arr[r+1][c]==0:
if vis.get((r+1,c),-1)==-1 or vis[(r+1,c)]!=True:
q.append((r+1,c))
x[r+1][c]=x[r][c]+1
if c-1>=0 and arr[r][c-1]==0:
if vis.get((r,c-1),-1)==-1 or vis[(r,c-1)]!=True:
q.append((r,c-1))
x[r][c-1]=x[r][c]+1
if c+1<m and arr[r][c+1]==0:
if vis.get((r,c+1),-1)==-1 or vis[(r,c+1)]!=True:
q.append((r,c+1))
x[r][c+1]=x[r][c]+1
#for i in x:
#print(i)
ans=x[0][m-1]
if ans==-1:
print(-1)
else:
print(ans)
答案 6 :(得分:0)
您的网格形成一个图形(或至少可以视为图形)。消除某些运动方向表明它是有向图。如果你不能从一个节点移动到另一个节点,那就是图中没有的边缘。
一旦您将网格编码为图形形式,就可以在众所周知的图形算法(您显然已经意识到)中进行选择,以便根据您想要的结果类型进行遍历(例如,最短的)路径)。
编辑:我看过你发布的答案,但我不确定该代码应该是什么/做什么。例如,它有:if(y>=0) max(abs(x),y);
。这似乎(至少对我而言)没有多大意义 - 来自max
的结果被简单地抛弃了。要完成一些有用的事情,需要返回或分配该命令。按照目前的情况,您可以希望最好的是编译器将其视为死代码,并且不会为其生成任何内容。
我的猜测是代码并没有真正按预期工作,如果它做了任何有用的事情,那么它比设计更偶然。需要花费大量的时间和精力来确保你已经解决了这样的问题,以至于你确实知道它做了什么,甚至更难以猜出真正意图的是什么。
答案 7 :(得分:-1)
使用A *算法查找2D网格中两点之间的路径。 http://theory.stanford.edu/~amitp/GameProgramming/ImplementationNotes.html