【问题标题】:Graph traversal specific verticies with conditions带有条件的图遍历特定顶点
【发布时间】:2012-09-15 11:47:42
【问题描述】:

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

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

  1. 每条边都在指定的时间窗口中打开以供遍历:Time Open1、Time Open2、TimeClose1、Time Close2 - 当前时间必须在这些时间间隔内才能遍历边。
  2. 只有一些顶点必须被访问。每个顶点必须在指定的时间窗口内至少访问一次:Time Open1、Time Open2、TimeClose1、Time Close2 - 当前时间必须在这些时间间隔内才能将顶点标记为已访问。
  3. 起点始终是顶点 0

对于我的例子,我有:
必须访问的顶点及其时间窗口(不考虑 -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 开始。在最短的时间段内找到最近的顶点进行标记(我使用 修改的 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
2. 我找到一条包含所有要标记的顶点的道路
3. 我发现一条路比另一条完整的路要花更长的时间
4. 我不能创建另一个节点,因为边缘是封闭的

解决方案 2:

应用@Boris Strandjev 示例,但我有一些问题:

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

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

编辑:

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

【问题讨论】:

  • 您是在寻找从顶点 0 到每个目标顶点的最短路径,还是从 0 开始并访问每个目标顶点的单个游览?后一个问题与旅行商问题有关,并且是 NP-hard。
  • 除了进行那些“往返”(0-1-0-1-5)之外,是否也可以在某个节点上等待直到所需的边缘可用?在这种情况下,您可以搜索忽略时间限制的路径,然后进行一些“后处理”以将等待时间添加到路径。
  • 不,您不能等待边或顶点可用,您必须在所有条件下找到最佳解决方案。只有当你穿过一条边时,时间才会前进。
  • @mUncescu:你所做的并不完全是 Dijkstra,它看起来更像是光束搜索。在 Dijkstra 中,增加路径的堆在每一步中都会扩展,但这并不意味着您丢弃旧的、未使用的路径。因此,第 3 步对您来说应该是{&lt;4, 010&gt;, 60}{&lt;5, 000&gt;, 60},因为它的时间比{&lt;2, 100&gt;, 110} 的时间短。总体而言,在第 2 步之后,您有 5 条扩充路径可供选择:{&lt;4, 010&gt;, 60}, {&lt;5, 000&gt;, 60}, {&lt;0, 100&gt;, 120} , {&lt;2, 100&gt;, 110}, {&lt;5, 100&gt;, 110}。对不起,也许我的例子有点令人困惑......

标签: algorithm graph graph-traversal


【解决方案1】:

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

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

立即查看
您将节点定义为三件事的组合: - 节点所指的城市。 - 访问时间 - 访问过的目标节点的位图(这样你就可以知道你是否已经访问过所有的目标)。

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

这是一个例子
你从&lt;city:=0, time:=0, bitmap:= (0 - true, 1...k - false)&gt;开始
如果您通过边缘 0-4,您会发现自己位于节点 &lt;city:=4, time:=60, bitmap:= ({0,4} - true, {1...k} / {4} - false)&gt;

继续以这种方式在节点之间移动,使用 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

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

正如您所知,在 Dijkstra 算法的过程中,您正在考虑增加路径(即您找到的到达已到达顶点前面的每个顶点的最佳路径)。我将按如下方式表示每个增广路径:(&lt;1,1100&gt;, 400) 表示您可以到达顶点&lt;1,1100&gt; 的当前最佳时间是 400。

您从一组扩充路径{(&lt;1, 1000&gt;, 0)} 和到所有顶点的距离infinity 开始算法。现在按照以下步骤操作。

以最佳增广路径到达第一个顶点。从它可能的边缘是1-21-31-4 在第 0 秒内不可用。它们触发了另外两个增强路径:{(&lt;2, 1100&gt;, 50), (&lt;3, 1010&gt;, 30)}&lt;1, 1000&gt; 的距离更改为 0。

下一步考虑最佳扩充路径(&lt;3, 1010&gt;, 30)。但是,可以使用较近的出边来添加增广路径:1-3 不能使用,因为在 60 时间无法访问顶点 1。边缘 3-4 也不能使用,因为时间间隔。因此,扩充路径现在是:{(&lt;2, 1100&gt;, 50)}

下一步:(&lt;2, 1100&gt;, 50) 和新的扩充路径:{(&lt;1, 1100&gt;, 100), (&lt;4, 1101&gt;, 70)}

下一步:(&lt;4, 1101&gt;, 70) 但它也没有添加新路径:顶点 2 在时间 90 不能访问,3-4 不能再使用。因此扩充路径是{(&lt;1, 1100&gt;, 100)}

下一步:(&lt;1, 1100&gt;, 100) 将扩充路径更改为:{(&lt;3, 1110&gt;, 130)}

下一步:(&lt;3, 1110&gt;, 130) 将扩充路径更改为:{(&lt;4, 1111&gt;, 180)}

下一步:(&lt;4, 1111&gt;, 180) 这是最后一步 - 我们处于访问所有目标顶点的状态。所以总结一下:你可以在180秒内访问所有的顶点。

我希望这个例子能帮助你理解我的想法。您可能需要在纸上写下所有注意事项,以确保我不会与扩充路径有关。

【讨论】:

  • @mUncescu:在我的解决方案中,您重新定义了“节点”,但后面您会执行普通的 Dijkstra 及其路径重建。你永远不需要重新访问一个节点(就像在普通的 Dijkstra 中一样)。在我的例子中,一个节点不仅代表一个顶点,还包括你用来到达这个顶点的路径的属性(因此是位图和时间)。这就是位图是每个节点的属性的原因:因为在全球范围内您有许多路径,而我只想在每个节点中本地化一个。
  • @mUncescu 阅读上面的帖子,如果您仍需要进一步的帮助 - 我会尽力提供。
  • 对于一个节点,你只保存你需要专门访问的那些,那么你把你的确切路径放在哪里?同样在最佳路径包含 0-4-2-4 的情况下,4 的最小距离不同,因此您访问的时间也不同,当我选择 Dijksta 中的最佳节点时,我的选择是基于最小距离,但在此如果我有多个最佳距离?你能提供一个更扩展的例子吗?
  • 基本上问题是我如何选择下一个要访问的节点,因为如果你在节点 4 你有 然后我进入 2 你有 那么如果我必须回到 4 我该怎么办?创建另一个节点?因为我改变了节点4的访问时间
  • @mUncescu:是的,抱歉我昨天没时间给你举个例子。我会尝试在今天某个时候添加它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多