由於這是無環有向圖上的最短路徑問題,因此可以使用標準shortest path algorithm。
您也可以使用動態規劃(「DP),這可能是最有效的優化技術。我的回答實現了DP算法。
最短路徑或DP算法將大大優於列舉所有路徑由於路徑數量隨着陣列大小呈指數增長,所以簡單的枚舉只能用於中等大小的陣列。
DP算法的思想如下:讓n
和m
分別是行數和列數,首先計算從最後一行中的每列到最後一列中的最短路徑最後一行。這是一個簡單的計算方法,因爲每個元素只有一條路徑爲[m-1, n-1]
。從[m-1, n-2]
開始,我們只需回到[m-1, 0]
即可。
接下來我們計算從倒數第二行(m-2
)開始並回到第一行(0
)開始的每個其他行中每個元素到[m-1, n-1]
的最短路徑。每行中的最後一個元素[i, n-1]
是一個簡單的計算,因爲只能往下(至[i+1, n-1]
)。因此,從[i, n-1]
到[m-1, n-1]
的最短路徑首先是[i+1, n-1]
,然後是從[i+1, n-1]
開始的最短路徑,這是我們已經計算出來的(當然包括它的長度)。從[i, n-1]
開始的最短路徑的長度是[i, n-1]
的「向下」距離加上從[i+1, n-1]
開始的最短路徑的長度。
對於元素[i, j]
,N-1 ,
我< j < m-1
,我們計算的最短路徑,如果我們走向右和向下,並選擇兩個短。
我們可以如下實現它。
代碼
def shortest_path(distance)
last_row, last_col = distance.size-1, distance.first.size-1
h = {}
last_row.downto(0) do |i|
last_col.downto(0) do |j|
h_right = { min_path_len: distance[i][j][:r] + h[[i,j+1]][:min_path_len],
next_node: [i,j+1] } if j < last_col
h_down = { min_path_len: distance[i][j][:d] + h[[i+1,j]][:min_path_len],
next_node: [i+1,j] } if i < last_row
g =
case
when i == last_row && j == last_col
{ min_path_len: 0, next_node: nil }
when i == last_row
h_right
when j == last_col
h_down
else
[h_right, h_down].min_by { |f| f[:min_path_len] }
end
h[[i,j]] = g
end
end
build_path(h)
end
def build_path(h)
node = [0, 0]
len = h[node][:min_path_len]
arr = []
while h[node][:next_node]
arr << node
node = h[node][:next_node]
end
[len, arr]
end
例
假設這些是相鄰節點之間的距離。
● 4 ● 3 ● 1 ● 2 ●
6 2 5 4 5
● 3 ● 4 ● 6 ● 3 ●
1 3 4 2 3
● 6 ● 3 ● 1 ● 2 ●
以散列數組的形式提供這些信息很方便。
distance = [
[{ r: 4, d: 6 }, { r: 3, d: 2 }, { r: 1, d: 5 }, { r: 2, d: 4 }, { d: 5 }],
[{ r: 3, d: 1 }, { r: 4, d: 3 }, { r: 6, d: 4 }, { r: 3, d: 2 }, { d: 3 }],
[{ r: 6 }, { r: 3 }, { r: 1 }, { r: 2 }]
]
我們現在可以計算最短路徑。
p shortest_path distance
#=> [15, [[0, 0], [0, 1], [1, 1], [2, 1], [2, 2], [2, 3]]]
最短路徑由返回的數組的第二個元素給出。 15
是該路徑的長度。