【问题标题】:Belman-Ford algorithm in 2d Array二维数组中的 Bellman-Ford 算法
【发布时间】:2016-01-27 12:02:30
【问题描述】:

我在将 Bellman-Ford 算法应用于 2D 数组(不是图形)时遇到问题

输入数组有 m x n 个维度:

           s[1,1] s[1,2] ... s[1,n] -> Exit
           s[2,1] s[2,2] ... s[2,n]
           ...
 Entry ->  s[m,1] s[m,2] ... s[m,n]

它与房间相似(每个条目都是一个房间,具有 s[x,y] 进入成本)。每个房间也可能有一个负成本,我们必须找到从入口到所选房间再到出口的最便宜的路径。

例如,我们有一系列房间和费用:

1   5   6
2  -3   4
5   2  -8

我们想走过房间[3,2]s[3,2] = 4。我们从5开始> 在 [1,3] 并且必须经过 [3,2] 才能到达 [3,3]

我的问题是,在 Bellman-Ford 算法中实现它的最佳方法是什么?我知道 Dijkstry 算法不会因为负成本而工作。

对于从 [0, maxHeight] 开始的每个房间并放松所有邻居是否正确?像这样:

   for (int i = height-1; i >= 0; --i) {
        for (int j = 0; j < width; ++j) {
            int x = i;
            int y = j;
            if (x > 0) // up
                Relax(x, y, x - 1, y);
            if (y + 1 < width) // right
                Relax(x, y, x, y + 1);
            if (y > 0) // left
                Relax(x, y, x, y - 1);
            if (x + 1 < height) // down
                Relax(x, y, x + 1, y);
        }
    }

但是我如何才能读取所选房间以及从房间到出口的费用?

【问题讨论】:

  • 如果你想知道最后的最优成本,你必须不断保存最优路径。你已经知道你要去哪个方向(你的 if 语句)所以在你去的时候保存那个信息......我认为你不能只使用你的二维数组来保存数据,你必须使用另一个二维数组或只添加字段到您当前的二维数组。你基本上需要保持前任值
  • 二维数组是表示图形的一种方式,称为邻接矩阵。
  • @turingcomplete 邻接矩阵可以存储在二维数组中,但并非所有二维数组都是邻接矩阵。 snot 图的邻接矩阵。它甚至不是正方形的。
  • @turingcomplete 虽然这会给你 a 邻接矩阵,但它仍然不是与这个问题相关的。问题的邻接矩阵为(m·n)×(m·n),不包含s作为子矩阵。
  • 您的示例中存在负循环。 Belman-ford(也不是任何其他算法)不会解决它。潜在地,您可以在 -8 和 -3 分数的房间之间永远走动以获得 -inifinity,

标签: c++ c algorithm graph


【解决方案1】:

如果您知道如何从数组中移动图表,则可以滚动到其他条件段落。另请阅读下一段。

实际上,您可以像在图表上一样查看该建筑物。

你可以看到:(我忘记了二线的门,抱歉。)

那么,如何实现。暂时忽略附加条件(离开前访问特定顶点)。
权重函数
S[][] 成为一个入口成本数组。注意,边的权重只决定最后的顶点。不管是(1, 2) -&gt; (1,3) 还是(2,3) -&gt; (1, 3)。成本由第二个顶点定义。所以函数可能看起来像:

cost_type cost(vertex v, vertex w) {
    return S[w.y][w.x];
}
//As you can see, first argument is unnecessary.

边缘
事实上,您不必将所有边保留在某个数组中。您可以在每次需要时在函数中计算它们。 顶点(x, y) 的邻居是(x+1, y)(x-1, y)(x, y+1)(x, y-1),如果这些节点存在的话。你必须检查它,但这很容易。 (检查 new_x > 0 && new_x

//Size of matrix is M x N
is_correct(vertex w) {
    if(w.y < 1 || w.y > M || w.x < 1 || w.x > N) {
        return INCORRECT;
    }
    return CORRECT;
}

生成邻居可能如下所示:

std::tie(x, y) = std::make_tuple(v.x, v.y);
for(vertex w : {{x+1, y}, {x-1, y}, {x, y+1}, {x, y-1}}) {
    if(is_correct(w) == CORRECT) {//CORRECT may be true
        relax(v, w);
    }
}

我相信,四个边不应该占用额外的内存。如果您不知道std::tie,请查看 cppreference。 (额外变量xy 占用更多内存,但我相信这里的可读性更高。在您的代码中可能不会出现。)

显然你必须有其他的二维数组与距离和(如果需要的话)前任,但我认为它很清楚,我不必描述它。

附加条件
你想知道从进入到退出的成本,但你必须访问一些顶点compulsory。最简单的计算方法是计算从entercompulsory 和从compulsoryexit 的成本。 (将有两个单独的计算。)它不会改变big O 时间。之后,您可以添加结果。

你只需要保证在compulsory之前访问exit是不可能的。这很简单,您可以通过在 is_correct 函数中添加额外的行(然后顶点v 将是必需的)或生成邻居代码片段来删除来自exit 的传出边。

现在你可以实现它basing on wikipedia。你有图表。

为什么你不应该听?
更好的方法是使用其他顶点的贝尔曼福特算法。请注意,如果您知道从 A 到 B 的最佳路径,那么您也知道从 B 到 A 的最佳路径。为什么?总是你必须为最后一个顶点付费而你不为第一个顶点付费,所以你可以忽略它们的成本。休息很明显。
现在,如果您知道您想知道路径 A->B 和 B->C,则可以使用节点 B 的一次 BF 和反向路径 B->A 计算 B->A 和 B->C。结束了。
您只需删除来自entryexit 节点的传出边。

但是,如果您需要非常快速的算法,则必须对其进行优化。但我认为这是另一个话题。此外,似乎没有人对硬优化感兴趣。
我可以快速添加,只是那个小而简单的优化基础,你可以忽略相应远距离顶点的松弛。在数组中,您可以轻松计算距离,因此这是令人愉快的优化。
我没有提到众所周知的优化,因为我相信它们都在网络的随机过程中。

【讨论】:

  • 存在很好的优化(使用它,该图是平面的),但我不能发誓,我会找时间来描述它。然而,问题是关于贝尔曼福特算法的简单实现。有人真的对这种优化感兴趣吗?如果没有,我不会考虑描述。
猜你喜欢
  • 2015-05-04
  • 2020-03-23
  • 1970-01-01
  • 1970-01-01
  • 2015-07-25
  • 1970-01-01
  • 1970-01-01
  • 2015-08-27
  • 1970-01-01
相关资源
最近更新 更多