繁体   English   中英

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

[英]How can I return all the shortest paths with tied weight using bellman-ford algorithm?

正如标题所说,我正在寻找的是打印按重量捆绑的“所有最短路径”。

例子:

我们有一个图,其边从 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;
}

更抽象地看这个,你有一些东西可以找到一个最小的东西,你想改变它,让所有其他的东西都同样小。 (在寻找最大的东西时,同样的原则也适用。不过,坚持“最小”有助于更容易解释。)

许多用于寻找极端事物的算法都有一些部分会询问“A 比 B 更极端”。 例如,您可能会看到如下代码:

if ( A < B )
    smallest = A;

请注意,它如何忽略关系( A == B ),就像它忽略更糟糕的结果( A > B )一样。 因此,您只会获得返回的最佳结果中的第一个。 所以这是要改变的。 但是,您不能简单地将A < B更改为A <= B ,因为在平局的情况下,这会将B替换为A ,就像在A是更好的结果时替换它一样。 (您只会得到最后一个返回的最佳结果。)这三种情况(小于、等于和大于)需要分别处理。

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

把这些放在一起,上面的代码可能会变成下面的样子(在改变了smallest的声明之后):

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() 不过,我会给出一个版本,在接收新结果的同时保留旧的 output(只是最短路径中的第一条)。 这与其说是一个建议,不如说是一个将新数据结构与旧数据结构联系起来的说明。

// Recursive function to print the path of (just) the first given vertex from source vertex.
void printPath(vector< vector<int> > const &parent, vector<int> const &vlist)
{
    if (vlist.empty())
        return;

    int v = vlist.front();
    printPath(parent, parent[v]);
    cout << v << " ";
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM