给定有限的移动集,最小的成本路径穿越网格,并且移动的成本在不同位置可能有所不同?

问题描述

这是您最常用的最短/成本最低的路径问题的一个转折,尽管我确实有一些解决此问题的一般思路,但让我有些困惑。我认为居住在这个Stack Overflow这个神圣地方的stem众神可能知道他们认为适合给予我解决方案,或者如果没有,他们可能仍然喜欢在这个问题上有所作为。

问题陈述:

存在一个与X和Y轴对齐的正方形网格,其尺寸为 n x m (其中(0,0)表示最左和最底角, (n-1,m-1)表示最右边和最上面的角)。 从任何给定位置开始,您都可以按照给定移动集定义的方向在一定方向上移动一定数量的步。 在此问题中,我们考虑一个移动集,该移动集允许从网格上的任意点选择以下四个移动中的任何一个

  • r1 :向右移动一个位置(平行于X轴+1个单位)。
  • r2 :向右移动两个位置(平行于X轴+2个单位)。
  • u1 :向上移动一个位置(平行于Y轴+1个单位)。
  • u2 :向上移动两个位置(平行于Y轴+2个单位)。

从任何给定位置(Xi,Yj),这些移动中的每一个都具有 r1 (Xi,Yj), r2 (Xi,Yj)给出的相关成本, u1 (Xi,Yj), u2 (Xi,Yj)。 (请注意,移动的成本可能取决于移动的位置而有所不同。)

您的目标是以最低的总成本从(0,0)移至(n-1,m-1)。
设计一个有效的算法来做到这一点。推导时间复杂度。

(problem statement attached image)

到目前为止我所考虑的:

简单的方法:计算从(0,0)到(n-1,m-1)的每条路径的总权重,然后选择成本最低的路径。
这种方法的问题:随着 n m 增加,几乎立即变得不可行。
有趣的是,似乎通过r1和r2的一些移动(无任何上移)从X = 0到X = n-1的总途径数为 Fibonacci( n-1 ,= Fibonacci序列的第n-1 个项(其中 Fibonacci( 0 = 0 , Fibonacci( 1 = 1, Fibonacci( 2 = 1等)。 u1和u2的情况相同,从Y = 0变为Y = m-1。虽然,我不确定考虑这个是否真的相关? ...
无论如何,很明显,将需要一种更有效的技术。

贪婪的方法:在每个步骤中做出局部最优选择(例如,只要选择不移动,则每回合在当前位置选择具有最佳(move_weight)/(spaces_moved)比率的移动不会超过n-1或m-1)。
这种方法的问题:时间复杂度很高,但是我不认为仅在每个位置选择局部最优选择就可以保证全局最优路径。例如,当前位置的局部最佳移动可能是权重为5的r1,然后是权重为8的下一个u2移动;但是在这种情况下,本回合移动u2的权重为7可以使我们将r1的权重为3进行下一步。然后,很明显,这种贪婪方法并不是全局最优的,因为当存在到同一目的地的成本为10的一条不同路径时,它会生成成本为13的路径。

...这就是我现在所在的位置。我正在考虑尝试某种涉及树木的解决方案(例如游戏树?),或者接下来是对Dijkstra最短路径算法的某种修改?任何解决方案或见解将不胜感激:)

解决方法

这是完全最短路径问题。您可以使用Dijkstra的算法。该算法包括存储从每​​个节点到目标节点(n-1,m-1)的“到目前为止已知的最短距离”。或者,存储从每个节点到起始节点的“到目前为止已知的最短距离”(0,0)。

因此,您创建了一个n * m数组来存储所有这些距离。

最初,您仅知道节点(n-1,m-1)与自身的距离为0;而且您不知道从任何其他节点到(n-1,m-1)的任何路径;因此,您将每个节点的距离初始化为(n-1,m-1)的距离为0,并将其他每个节点的距离初始化为+ Infinity。

然后迭代地选择一个节点,并通过窥视其邻居并选择最有用的邻居来更新已知的最短节点:

dist[x,y] = min(
                 dist[x,y],dist[x+1,y] + cost_r1[x,// being careful if x+1 is out of bounds
                 dist[x+2,y] + cost_r2[x,// being careful if x+2 is out of bounds
                 dist[x,y+1] + cost_u1[x,// being careful if y+1 is out of bounds
                 dist[x,y+2] + cost_u2[x,// being careful if y+2 is out of bounds
                )

Dijkstra的算法停止条件基本上是“持续更新,直到没有任何更新”。这可以通过多种方式实现。

在我们的例子中,我们知道图形是一个网格,并且我们只知道向右或向上移动,因此我们可以聪明地按照正确的顺序直接更新距离;我们唯一需要确定的是,在计算与(x,y)的距离时,我们必须已经计算出与(x + 1,y),(x + 2,y),(x, y + 1)和(x,y + 1)。

由于我们必须特别注意+1和+2不能使我们脱离数组,因此您可以先处理n-1和n-2列以及m-1和m-2行,然后再处理数组的其余部分,或者您可以编写包装函数dist(x,y),如果存在,则返回dist[x,y];如果x或y超出范围,则返回+ Infinity。

下面,我选择不使用包装器函数,而是为并非所有四个选项u1,u2,r1,r2都可用的单元格制作了单独的循环。

dist[n-1,m-1] = 0

dist[n-2,m-1] = cost_r1[n-2,m-1]
for (x = n-3; x >= 0; x--)
    dist[x,m-1] = min(dist[x+1,m-1] + cost_r1[x,m-1],dist[x+2,m-1] + cost_r2[x,m-1])

dist[n-1,m-2] = cost_u1[n-1,m-2]
for (y = m-3; y >= 0; y--)
    dist[n-1,y] = min(dist[n-1,y+1] + cost_u1[n-1,dist[n-1,y+2] + cost_u2[n-1,y])

dist[n-2,m-2] = min(...r1,...u1)
for (x = n-3; x >= 0; --x)
    dist[x,...r2,...u1)
for (y = m-3; y >= 0; --y)
    dist[n-2,y] = min(...r1,...u1,...u2)

for (x = n-3; x >= 0; x--)
    for (y = m-3; y >= 0; y--)
        dist[x,...u2)

完成此操作后,您在数组dist的每个单元格中都具有从(x,y)到(n-1,m-1)的距离。特别是,单元格(0,0)包含从(0,0)到(n-1,m-1)的距离。

然后,如果您不仅对距离感兴趣,而且对实际走的路径感兴趣,则可以使用此数组在线性时间内检索它,方法是从(0,0)开始并导航到相邻单元格,这将成本降到最低( ie ,将由min(...,...,...)操作选择)。