Bellman-Ford 的负循环
Negative Cycles in Bellman-Ford
在具有 V 个节点和 E 条边的有向图中,Bellman-Ford 算法将每个顶点(或者更确切地说,从每个顶点发出的边)松弛 (V - 1) 次。这是因为从源到任何其他节点的最短路径最多包含 (V - 1) 条边。在第V次迭代中,如果一条边可以松弛,说明存在负循环。
现在,我需要通过这个负循环找到其他节点"ruined"。也就是说,一些不在负循环上的节点现在与源的距离为负无穷大,因为从源到位于负循环中的节点的路径上有一个或多个节点。
完成此操作的一种方法是 运行 Bellman-Ford 并记下负循环上的节点。然后,从这些节点中运行DFS/BFS标记其他节点。
但是,为什么我们不能 运行 Bellman-Ford 2 * (V - 1) 次而不求助于 DFS/BFS 来检测此类节点?如果我的理解是正确的,那么将所有顶点放松 2 * (V - 1) 次应该允许负循环 "propagate" 它们的值到所有其他连接的节点。
补充说明:我在解决这个在线问题时遇到了这种情况:https://open.kattis.com/problems/shortestpath3
我使用的Java代码(以及此处未显示的BFS/DFS)如下:
// Relax all vertices n - 1 times.
// And relax one more time to find negative cycles
for (int vv = 1; vv <= n; vv++) {
// Relax each vertex
for (int v = 0; v < n; v++) {
// For each edge
if (distTo[v] != (int) 1e9) {
for (int i = 0; i < adjList[v].size(); i++) {
int dest = adjList[v].get(i).fst;
int wt = adjList[v].get(i).snd;
if (distTo[v] + wt < distTo[dest]) {
distTo[dest] = distTo[v] + wt;
if (vv == n) {
isInfinite[v] = true;
isInfinite[dest] = true;
}
}
}
}
}
}
在经典情况下,所有节点 "on" 负长度循环与源的距离任意小。
因此,在第 v-1 次之后的每次迭代中,从源到此类节点的路径都会变小。
该任务要求您对所有此类节点 return -infinity。
您可以使用 Bellman-Ford 算法的修改版本将所有此类节点的距离标记为 -infinity 和 运行 它 v-1 次以使 -infinity 传播到连接到的所有其他节点周期。但是与循环中节点的 运行 DFS 或 BFS 相比,这需要很多额外的时间。
考虑具有 N=4, M=5
的图表:
A -> B weight 1000
A -> C weight 1000
C -> D weight -1
D -> C weight -1
D -> B weight 1000
设 A 为源,B 为目的地。
现在显然有一个负循环(C <-> D)
。但是无论我们运行算法N次还是2N次甚至3N次,从A到B的最短路径还是1000。由于负循环每次使用都只是减少了一点距离,所以不会像我们预期的那样传播到其他节点。
一种解决方案是,一旦确定了影响节点的循环,就将距离标记为负无穷大。这样,通过其他节点的其他最短路径的负循环 "takes precendence"。
此致,
一位在这个问题上花了很多时间的编码员。
在具有 V 个节点和 E 条边的有向图中,Bellman-Ford 算法将每个顶点(或者更确切地说,从每个顶点发出的边)松弛 (V - 1) 次。这是因为从源到任何其他节点的最短路径最多包含 (V - 1) 条边。在第V次迭代中,如果一条边可以松弛,说明存在负循环。
现在,我需要通过这个负循环找到其他节点"ruined"。也就是说,一些不在负循环上的节点现在与源的距离为负无穷大,因为从源到位于负循环中的节点的路径上有一个或多个节点。
完成此操作的一种方法是 运行 Bellman-Ford 并记下负循环上的节点。然后,从这些节点中运行DFS/BFS标记其他节点。
但是,为什么我们不能 运行 Bellman-Ford 2 * (V - 1) 次而不求助于 DFS/BFS 来检测此类节点?如果我的理解是正确的,那么将所有顶点放松 2 * (V - 1) 次应该允许负循环 "propagate" 它们的值到所有其他连接的节点。
补充说明:我在解决这个在线问题时遇到了这种情况:https://open.kattis.com/problems/shortestpath3
我使用的Java代码(以及此处未显示的BFS/DFS)如下:
// Relax all vertices n - 1 times.
// And relax one more time to find negative cycles
for (int vv = 1; vv <= n; vv++) {
// Relax each vertex
for (int v = 0; v < n; v++) {
// For each edge
if (distTo[v] != (int) 1e9) {
for (int i = 0; i < adjList[v].size(); i++) {
int dest = adjList[v].get(i).fst;
int wt = adjList[v].get(i).snd;
if (distTo[v] + wt < distTo[dest]) {
distTo[dest] = distTo[v] + wt;
if (vv == n) {
isInfinite[v] = true;
isInfinite[dest] = true;
}
}
}
}
}
}
在经典情况下,所有节点 "on" 负长度循环与源的距离任意小。 因此,在第 v-1 次之后的每次迭代中,从源到此类节点的路径都会变小。 该任务要求您对所有此类节点 return -infinity。
您可以使用 Bellman-Ford 算法的修改版本将所有此类节点的距离标记为 -infinity 和 运行 它 v-1 次以使 -infinity 传播到连接到的所有其他节点周期。但是与循环中节点的 运行 DFS 或 BFS 相比,这需要很多额外的时间。
考虑具有 N=4, M=5
的图表:
A -> B weight 1000
A -> C weight 1000
C -> D weight -1
D -> C weight -1
D -> B weight 1000
设 A 为源,B 为目的地。
现在显然有一个负循环(C <-> D)
。但是无论我们运行算法N次还是2N次甚至3N次,从A到B的最短路径还是1000。由于负循环每次使用都只是减少了一点距离,所以不会像我们预期的那样传播到其他节点。
一种解决方案是,一旦确定了影响节点的循环,就将距离标记为负无穷大。这样,通过其他节点的其他最短路径的负循环 "takes precendence"。
此致,
一位在这个问题上花了很多时间的编码员。