我有一个类似下面的无向图,我需要实现一个图遍历算法。
示例:
http://i.imgur.com/15L6m.png
这个想法是每个顶点都是一个城市,每个边都是一条道路 边的权重表示遍历指定边所需的时间 条件是:
对于我的例子,我有:
必须访问的顶点及其时间窗口(不考虑-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,允许在间隔之外的访问但不标记。对于一个顶点我有{(< id1,id2id3id4>,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
修改:
我最终使用了上述两种解决方案的组合。一切似乎都很好。
答案 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-2
和1-3
(1-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秒。
我希望这个例子可以帮助你理解我的想法。您可能需要在纸张上写下所有注意事项,以使其不会受到增强路径的影响。