如何使用Bellman-ford算法返回所有具有权重的最短路径?

时间:2019-11-13 21:01:48

标签: c++

正如标题所述,我正在寻找的是打印受重量限制的“所有最短路径”。

示例:

我们有一个图,其边线从0-> 1-> 3的权重为6,但是我们也有路径0-> 3的权重也为6,以下算法仅返回第一个路径,我想知道是否可以同时返回两个或所有路径。还有一种更有效/更优雅的方式来打印最短路径。我仅以此代码为例,我的代码与之非常相似,但仅从源到最后一个顶点打印。

这里回答了类似的问题,但是由于我熟悉c ++,所以我无法理解代码。

#include <iostream>
#include <vector>
#include <iomanip>
#include <climits>
using namespace std;

// Data structure to store graph edges
struct Edge
{
    int source, dest, weight;
};

// Recurive Function to print path of given vertex v from source vertex
void printPath(vector<int> const &parent, int v)
{
    if (v < 0)
        return;

    printPath(parent, parent[v]);
    cout << v << " ";
}

// Function to run Bellman Ford Algorithm from given source
void BellmanFord(vector<Edge> const &edges, int source, int N)
{
    // count number of edges present in the graph
    int E = edges.size();

    // distance[] and parent[] stores shortest-path (least cost/path)
    // information. Initially all vertices except source vertex have
    // a weight of infinity and a no parent

    vector<int> distance (N, INT_MAX);
    distance[source] = 0;

    vector<int> parent (N, -1);

    int u, v, w, k = N;

    // Relaxation step (run V-1 times)
    while (--k)
    {
        for (int j = 0; j < E; j++)
        {
            // edge from u to v having weight w     
            u = edges[j].source, v = edges[j].dest;
            w = edges[j].weight;

            // if the distance to the destination v can be
            // shortened by taking the edge u-> v
            if (distance[u] != INT_MAX && distance[u] + w < distance[v])
            {
                // update distance to the new lower value
                distance[v] = distance[u] + w;

                // set v's parent as u
                parent[v] = u;
            }
        }
    }

    // Run Relaxation step once more for Nth time to
    // check for negative-weight cycles
    for (int i = 0; i < E; i++)
    {
        // edge from u to v having weight w
        u = edges[i].source, v = edges[i].dest;
        w = edges[i].weight;

        // if the distance to the destination u can be
        // shortened by taking the edge u-> v       
        if (distance[u] != INT_MAX && distance[u] + w < distance[v])
        {
            cout << "Negative Weight Cycle Found!!";
            return;
        }
    }

    for (int i = 0; i < N; i++)
    {
        cout << "Distance of vertex " << i << " from the source is "
             << setw(2) << distance[i] << ". It's path is [ ";
        printPath(parent, i); cout << "]" << '\n';
    }
}

// main function
int main()
{
    // vector of graph edges as per above diagram
    vector<Edge> edges =
    {
        // (x, y, w) -> edge from x to y having weight w
        { 0, 1, 2 }, { 1, 3, 4 }, { 0, 3, 6 }
    };

    // Set maximum number of nodes in the graph
    int N = 5;

    // let source be vertex 0
    int source = 0;

    // run Bellman Ford Algorithm from given source
    BellmanFord(edges, source, N);

    return 0;
}

1 个答案:

答案 0 :(得分:0)

更抽象地看待这个问题,您可以找到最小的东西,并且想要对其进行更改以将其他所有同样小的东西返回。 (当寻找最大的东西时,同样的原则也适用。不过,坚持“最小”的做法有助于做出更简单的解释。)

许多用于查找极端事物的算法在某些方面要求“是A比B更极端”。例如,您可能会看到类似以下的代码:

if ( A < B )
    smallest = A;

请注意,它如何忽略联系(A == B)的方式与忽略更差结果(A > B)的方式相同。因此,您只会得到返回的最佳结果中的第一个。所以这是要改变的事情。但是,您不能简单地将A < B更改为A <= B,因为在平局的情况下将B替换为A,就像在{{1 }}是更好的结果。 (返回的最佳结果只有 last 。)这三种情况(小于,等于和大于)需要分别处理。

另一个要看的方面是如何跟踪最小的事物。上面的代码段表明Asmallest具有相同的类型;这不足以跟踪多个解决方案。您可能需要一个容器来跟踪解决方案。 A可能是一个合理的选择。

将它们放在一起,上面的代码可能会变成类似以下内容(在更改vector的声明之后):

smallest

如何将其应用于Bellman-Ford?

幸运的是,代码的关键部分相对容易,因为有注释记录在其中。较难的部分是更改代码以跟踪多个结果,因为找到较短的路径时会更新两个数据。看来if ( A < B ) { smallest.clear(); smallest.push_back(A); } else if ( A == B ) { smallest.push_back(A); } 是需要扩展的数据。这是它的新声明:

parent

这使用空向量代替vector< vector<int> > parent (N); 来表示“无父”。现在可以成为最短路径的检查

-1

这与常规方法略有不同,因为没有“ if (distance[u] != INT_MAX) { // if the distance to the destination v can be // shortened by taking the edge u-> v if (distance[u] + w < distance[v]) { // update distance to the new lower value distance[v] = distance[u] + w; // forget the previous parent list. parent[v].clear(); } // if u-> v is a way to get the shortest // distance to the destination v. if (distance[u] + w == distance[v]) { // add u as a possible parent for v parent[v].push_back(u); } } ”。这是相同的逻辑,只是排列方式有所不同。请注意,当输入第一个else子句时,距离矢量会更新,因此也要输入第二个if子句。

我认为处理找到的路径是一个单独的(而不是琐碎的)问题,因此我将留给您了解如何更新if。不过,我将给出一个在接收新结果的同时保留旧输出(只是最短路径的第一个)的版本。这并不是建议,只是说明新数据结构与旧数据结构有关。

printPath()