在有效时间内找到通过图表的最短路径,其中附加约束条件必须包含路径 n 节点。
我们有一个有向加权图。它可能包含也可能不包含循环。我们可以使用Dijkstra算法轻松找到最短路径,但Dijkstra不保证边缘数量。
我们能想到的最好的方法是保留一个节点的最佳n路径列表,但这比vanilla Dijkstra的内存使用了大量内存。
答案 0 :(得分:7)
这是一种简单的动态编程算法。
让我们假设我们想要从顶点x到顶点y。
制作表D [。,。],其中D [v,k]是从根到顶点v的最短路径长度为k的成本。
Initially D[x,1] = 0. Set D[v,1] = infinity for all v != x.
For k=2 to n:
D[v,k] = min_u D[u,k-1] + wt(u,v), where we assume that wt(u,v) is infinite for missing edges.
P[v,k] = the u that gave us the above minimum.
最便宜路径的成本将存储在D [y,n]中。
如果我们有一个边缘较少的图形,我们可以通过搜索v所连接的u来有效地完成此操作。这可以通过一系列邻接列表以最佳方式完成。
恢复最便宜的路径:
Path = empty list
v = y
For k= n downto 1:
Path.append(v)
v = P[v,k]
Path.append(x)
Path.reverse()
最后一个节点是y。之前的节点是P [y,n]。我们可以继续跟进,我们最终会得到P [v,2] = x的某些v。
答案 1 :(得分:0)
我想到的另一种选择是深度优先搜索(与Dijkstra的广度优先搜索相反),修改如下:
如果超出所需的顶点数,则停止“深度” -
记录具有正确节点数的最短路径(到目前为止)。
运行时间可能很糟糕,但在使用非常合理的内存量时应该会得到正确的结果。
答案 2 :(得分:0)
有趣的问题。您是否讨论过使用启发式图搜索(例如A *),为超过或低于节点数添加惩罚?这可能会或可能不会被接受,但如果确实有效,则可能比保留所有潜在路径的列表更有效。
事实上,您可以使用回溯来限制用于您所讨论的Dijkstra变体的内存量。
答案 3 :(得分:0)
算法的粗略概念:
设A为起始节点,让S为一组节点(加上路径)。不变量是在步骤n结束时,S将是与A完全相距n步的所有节点,并且路径将是该长度的最短路径。当n为0时,该集合为{A(空路径)}。在步骤n-1给出这样的设置,你可以从空集S1和
开始步骤nfor each (node X, path P) in S
for each edge E from X to Y in S,
If Y is not in S1, add (Y, P + Y) to S1
If (Y, P1) is in S1, set the path to the shorter of P1 and P + Y
只有n个步骤,每个步骤应该小于max(N,E),这使得 密集图的整个算法O(n ^ 3)和稀疏图的O(n ^ 2)。
这个算法来自于Dijkstra,虽然它是一个不同的算法。
答案 4 :(得分:-1)
假设我们想要从节点x到k步的y的最短距离 简单的dp解决方案就是
A [k] [x] [y] = min {A [1] [i] [k] + A [t-1] [k] [y]} k从0变化到n-1
A[1][i][j] = r[i][j]; p[1][i][j]=j;
for(t=2; t<=n; t++)
for(i=0; i<n; i++) for(j=0; j<n; j++)
{
A[t][i][j]=BG; p[t][i][j]=-1;
for(k=0; k<n; k++) if(A[1][i][k]<BG && A[t-1][k][j]<BG)
if(A[1][i][k]+A[t-1][k][j] < A[t][i][j])
{
A[t][i][j] = A[1][i][k]+A[t-1][k][j];
p[t][i][j] = k;
}
}
trace back the path
void output(int a, int b, int t)
{
while(t)
{
cout<<a<<" ";
a = p[t][a][b];
t--;
}
cout<<b<<endl;
}