我应该使用哪个boost图算法?

6
我有一组节点A-G、Z,定义了加权边,其中A-G是漏斗中的各种节点,Z在最底部。将漏斗(V形)可视化,具有各种边缘,但最终指向Z,即水流向单个点Z。我们希望找到最便宜的路径到达Z,该路径覆盖了漏斗中的所有节点。
以下是约束条件:
- 没有孤立节点(所有节点都连接/包含) - 我们希望最小化加权边的总和 - “共享边缘”(如水向下流动时合并),仅计算共享边缘的权重一次(换句话说,沿着湿润的路径自由向下流动)
应使用哪个增强图算法来查找此问题的最佳边集?
  • A-B-D-E-Z是一条涵盖许多节点的廉价路径
  • C-G-Z有点强制,因为G只有一条通往Z的路径
  • F-Z看起来很便宜,但是我们注意到由于C-G-Z是强制性的,所以F-G-Z实际上比F-Z更便宜(因为我们不需要双重计算G-Z段,F-G的增量成本仅为1)

因此,边集应为(A-B、B-D、D-E、E-Z、C-G、F-G、G-Z)

我确定这不是一个新问题:我只是不了解足够的图论来识别/命名算法。

Directed Acyclic Graph

更新

在进一步研究问题时,我发现如果图形不是有向的,那么问题就简化为最小生成树。换句话说,如果我们没有事先指定 Z 是图中最低点(通过箭头使用),并且允许水在两个方向上流动(通常是真实的,除非我们有阀门),那么这个第二个模型将工作得很好。

NonDirected Acyclic Graph

当然,我们现在可以选择较小权重的新F-Z无向边,而不是被迫使用旧的G-Z有向边

考虑到这些结果,如果我们确实需要边是有向的liori's answer 是对原问题(即需要编写算法)的最佳回应。

输出

D <--> E with weight of 1
F <--> G with weight of 1
A <--> B with weight of 2
E <--> Z with weight of 2
C <--> G with weight of 2
F <--> Z with weight of 2
B <--> D with weight of 3
Total Weight = 13

使用最小生成树的无向无环图代码
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/kruskal_min_spanning_tree.hpp>
#include <iostream>

int
main()
{
  using namespace boost;

  typedef adjacency_list < vecS, vecS, undirectedS,
    no_property, property < edge_weight_t, int > > Graph;
  typedef graph_traits < Graph >::edge_descriptor Edge;
  typedef graph_traits < Graph >::vertex_descriptor Vertex;
  typedef std::pair<int, int> E;

  char letter[] = "ABCDEFGZ";
  const int num_nodes = 8;
  E edge_array[] = { 
        E(0,1), E(1,2), E(1,3), E(3,6), E(3,5), E(3,4), E(2,5), E(2,6), 
        E(5,7), E(5,6), E(6,7), E(4,7) 
  };
  int weights[] = { 2, 6, 3, 5, 3, 1, 4, 2, 2, 1, 3, 2 };
  std::size_t num_edges = sizeof(edge_array) / sizeof(E);
  Graph g(edge_array, edge_array + num_edges, weights, num_nodes);
  property_map < Graph, edge_weight_t >::type weight = get(edge_weight, g);
  std::vector < Edge > spanning_tree;

  kruskal_minimum_spanning_tree(g, std::back_inserter(spanning_tree));

  int total_weight = 0;
  for (std::vector < Edge >::iterator ei = spanning_tree.begin();
       ei != spanning_tree.end(); ++ei) 
  {
    std::cout << letter[source(*ei, g)] << " <--> " << letter[target(*ei, g)]
      << " with weight of " << weight[*ei]
      << std::endl;
    total_weight += weight[*ei];
  }
  std::cout << "Total Weight = " << total_weight << std::endl;

  return EXIT_SUCCESS;
}

你决定使用哪个算法了吗?如果是的话,在Boost中找到匹配的算法应该很容易。建议:也将其标记为“图形”和“算法”,并将其作为有关算法的一般问题。 - Ulrich Eckhardt
@doomster 感谢您的标签建议 - 不幸的是,我的图论背景不够强大,无法识别/命名正确的算法(但我可以描述问题)。 - kfmfe04
你想要一个覆盖所有节点的路径吗?你有你想要的图片吗? - phant0m
@phant0m 不是的 - 路径可以分支/合并(就像水流进漏斗一样,但通常来说,分支会花费更多),但每个节点都必须以某种方式找到其下行到 Z 的路径 - 我会尝试想出一个示例情况,这样更容易理解:漏斗水是我目前最接近的类比。 - kfmfe04
啊,我说的是这个 - phant0m
@phant0m - 好主意:专注于一个。 - kfmfe04
2个回答

3
所以,您需要最便宜的方法从Z向每个节点倒退。您的问题等同于在DAG上找到一棵生成树,只是您需要转换您的DAG,使边指向相反的方向。如this answer所解释的那样,您应该检查算法,例如Chu-Liu / Edmonds算法
尽管如此,在Boost Graph中似乎没有现成的算法可用。您可能需要构建自己的算法。

谢谢你的提示,我正在调查是否使用最小生成树可以解决问题,当你发布了答案。我可能需要尝试编写一个最小生成树来看它是否会给我正确的答案。也许Z节点就像其他节点一样,并且无向图就足够了。+1 - kfmfe04
@phant0m:只是因为问题的通常表述是沿着边的方向前进,而这里你需要往相反的方向前进。 - liori

1
这是一个可以用一致代价搜索来解决的问题。该算法可应用于任何至少包含一个解的有向图
这将找到最低总边缘成本的路径。
如果您正在寻找涵盖最少节点的路径,则需要使用广度优先搜索

然而,棘手的部分是我不仅仅寻找从A到Z的一条路径:我需要最便宜的总解决方案,以便所有节点都能到达Z(这不会重复计算任何边缘)。 - kfmfe04
@kfmfe04 那我鼓励您去研究一下旅行商问题 - Kyle
TSP过于限制解决方案:树允许分支(对我的问题而言是有效的解决方案),而TSP则不允许。 - kfmfe04

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接