图形遍历具有条件的特定顶点

时间:2012-09-15 11:47:42

标签: algorithm graph graph-traversal

我有一个类似下面的无向图,我需要实现一个图遍历算法。 示例
http://i.imgur.com/15L6m.png

这个想法是每个顶点都是一个城市,每个边都是一条道路 边的权重表示遍历指定边所需的时间 条件是:

  1. 在指定的时间窗口内打开每条边进行遍历:时间打开1,时间打开2,时间关闭1,时间关闭2 - 当前时间必须在这些间隔中才能遍历边缘。
  2. 只能访问一些顶点。必须在指定的时间窗口中对每个顶点访问顶点一次:时间Open1,时间Open2,TimeClose1,时间Close2 - 当前时间必须在这些间隔中才能将顶点标记为已访问。
    < / LI>
  3. 起点始终是顶点0
  4. 对于我的例子,我有:
    必须访问的顶点及其时间窗口(不考虑-1的值):

    Vertex To1  Tc1  To2  Tc2
      1    0    260  340  770
      4    0    240  -1   -1 
      5    170  450  -1   -1 
    

    在以下时间窗口中打开边缘(不带-1的值 考虑):

    Edge To1  Tc1  To2  Tc2
    0-1  0    770  -1   -1 
    0-4  0    210  230  770
    0-5  0    260  -1   -1 
    1-2  0    160  230  770
    1-5  40   770  -1   -1 
    2-4  80   500  -1   -1 
    3-4  60   770  -1   -1 
    3-5  0    770  -1   -1 
    

    所以基本思路是从顶点0开始,找到最短的遍历路径 顶点1,4和5考虑到指定的时间。
    另外,例如,如果你做了0-1而你不能使用1-5你可以做0-1-0-1-5。

    我现在正在使用的一个可能的解决方案是:
    从0开始。在最短的时间内找到要标记的最近顶点(我使用a 改进的Dijkstra算法)。这样做直到我标记了所有需要的顶点。
    问题是我认为我没有找到所有可能性,因为正如我所说 你也可以像0-1-0-1-5组合一样四处移动,最后你可能会选择一条更短的路线。

    为了使它更清晰,我必须找到最短的路径,这样我从顶点0开始,以一个目标顶点结束,而我已经访问了所有其他目标顶点,至少一次尊重强加在边缘上的条件和目标顶点。
    例如:
    可能的解决方案是0 - 4 - 3 - 5 - 1,总时间为60 + 50 + 60 + 50 = 220 从0开始我也可以直接转到5但是如条件中所述,以便标记顶点5 我必须有170到450之间的累积时间。另外如果我去0-4我不能使用边缘4-2因为它打开80并且我的累积时间是60.注意我可以使用0-4-3因为4 -3在60处打开并且在0-4处打开需要等于60的时间。

    首先,我将使用最多20个顶点和 最多约50条边

    解决方案1:
                 0
          1 4 5     0 2 5 0 2 3 0 1 3

    我所做的是通过访问与树相似的每个相邻顶点来遍历图形。我在以下情况下停止扩展分支:
    1.我有太多重复项,比如我有0 1 0 4 0 1 0 - 所以我停下来因为我有一组重复的0值,即4 我发现一条道路包含了所有要标记的顶点 我发现一条路比另一条完整的路要长 4.由于边缘已关闭,我无法创建另一个节点

    解决方案2:

    应用@Boris Strandjev的例子,但我遇到了一些问题:

    我必须在他们的间隔中至少访问一次节点1,4和5,允许在间隔之外的访问但不标记。对于一个顶点我有{(&lt; id1,id2id3id4&gt;,time)},其中id1是当前顶点的ide,而id2-4表示1,4,5的bool值,如果在指定的时间间隔内访问,时间 - 路径中的当前时间

    Step1: 
    {<0, 000>, 0} I can visit - {<1, 100>, 60} - chosen first lowest val
                              - {<4, 010>, 60}
                              - {<5, 000>, 60}
    Step2:
    {<1, 100>, 60} - {<0, 100>, 120} 
                   - {<2, 100>, 110}   - chosen 
                   - {<5, 100>, 110}    
    Step3:
    {<2, 100>, 110} - {<1, 100>, 160} - if I choose 1 again I will have a just go into a loop 
                    - {<4, 110>, 170}   
    Step4:
    {<4, 110>, 170} - {<0, 110>, 230}
                    - {<2, 110>, 230} 
                    - {<3, 110>, 220}   - chosen
    
    Step5:
    {<3, 110>, 220} - {<4, 110>, 270} - again possible loop
                    - {<5, 111>, 280} 
    Step6:
    {<5, 111>, 280} - I stop Path: 0-1-2-4-3-5 cost 280
    

    修改:

    我最终使用了上述两种解决方案的组合。一切似乎都很好。

1 个答案:

答案 0 :(得分:1)

我没有看到对图表中顶点或边缘数量的严格限制,所以请原谅我,如果我的解决方案不适合你。如果您需要任何改进,请给予更严格的限制。

一种可能的解决方案是扩展节点的定义。不要仅仅将城市视为图表中的节点,而是添加更多属性。隐藏边缘定义,随时生成传出边缘,以便节省内存。

现在看
您可以将节点定义为三件事的组合:   - 节点所指的城市。   - 访问时间   - 访问目标节点的位图(以便您可以判断您是否已访问过所有目标)。

现在边缘有点复杂 - 它们会引导您从一个城市到另一个城市,但每个边缘也会改变相邻节点的时间。还要继续每步更新目标节点位图。

以下是一个例子
你从<city:=0, time:=0, bitmap:= (0 - true, 1...k - false)>开始 如果你走到0-4边缘,你会发现自己处于节点<city:=4, time:=60, bitmap:= ({0,4} - true, {1...k} / {4} - false)>

继续以节点之间的方式移动,使用Dejkstra算法,你会找到你的解决方案(当你扩展节点定义时,现在甚至会考虑圆形)。每当您发现自己位于其位图中所有位都已设置的节点时,您就找到了解决方案。

您将在此类解决方案中使用的节点数量不是那么容易计算,但是对于相对有限的节点数量和相当有限的目标城市数量,它应该可以工作(问题是结果节点的数量是指数级的目标城市)。

修改

以下是您要求的扩展示例:

想象一下你有这样的输入(我正在使用你的记号):

 Vertex To1  Tc1  To2  Tc2
   1    0    40   80  120
   2    40   80   -1   -1
   3    0   400   -1   -1 
   4    30   80   120 190

 Edge To1  Tc1  Weight
 1-2   0    770  50
 1-4  30     70  30
 1-3   0    400  30
 3-4 100    200  50
 2-4   0    400  20

我将用以下形式表示顶点: <1,1100>含义:当前顶点为1,第二个:在找到的路径中已访问第一个和第二个顶点。 到每个顶点的距离将是到达此顶点所需的最短时间。

正如您所知,在Dijkstra算法的过程中,您正在考虑增加路径(意味着您找到的已到达顶点前面的每个顶点的最佳路径)。我将按照以下方式拒绝每个扩充路径:(<1,1100>, 400)意味着您可以到达顶点<1,1100>的当前最佳时间是400。

您可以使用一组扩充路径{(<1, 1000>, 0)}和距所有顶点infinity的距离来启动算法。现在按照接下来的步骤进行操作。

使用最佳扩充路径到达第一个顶点。从它可能的边缘1-21-31-4在第0秒不可用。它们会触发另外两条扩充路径:{(<2, 1100>, 50), (<3, 1010>, 30)}到{{1}的距离}被更改为0。

下一步考虑最佳扩充路径<1, 1000>。然而,可以使用更近的输出边缘来添加扩充路径:(<3, 1010>, 30)不能使用,因为在时间60不能访问顶点1.边缘1-3也不能被使用,因为时间间隔。因此,扩充路径现在是:3-4

下一步:{(<2, 1100>, 50)}和新的扩充路径:(<2, 1100>, 50)

下一步:{(<1, 1100>, 100), (<4, 1101>, 70)}但它也不会添加新路径:在时间90处无法访问顶点2而且无法再使用(<4, 1101>, 70)。因此,扩充路径为3-4

下一步:{(<1, 1100>, 100)}会将扩充路径更改为:(<1, 1100>, 100)

下一步:{(<3, 1110>, 130)}会将扩充路径更改为:(<3, 1110>, 130)

下一步:{(<4, 1111>, 180)}这是最后一步 - 我们处于一个访问所有目标顶点的状态。总结一下:你可以访问限制中的所有顶点180秒。

我希望这个例子可以帮助你理解我的想法。您可能需要在纸张上写下所有注意事项,以使其不会受到增强路径的影响。