如何解决有障碍的图算法DFS / BFS的实现
我想问一下您是否可以给我有关基于图形算法(DFS或BFS)的任务的建议。 任务背后有一个故事。在宇宙中,有很多行星。其中一些行星被危险病毒感染,杀死所有人。 任务是找到从一个行星到一个药物行星的道路,因此有可能治愈这种病毒。 由于病毒,找到路径非常危险。机组人员可能去了病毒所在的星球(他们知道病毒在哪个星球上),但他们去了那里是因为他们别无选择,因为他们必须找到药物。在被感染的星球上,机组人员被感染了,并且有一些生存时间(时间是从一个星球到另一个星球的时间)。该任务的结果应该是从起始行星到存在药物的行星的路径。它应该适用于任何已知宇宙图。
一个已知行星与行星的例子。起始行星是0,终止行星是13。如您所见,行星1、2、4被感染。在此特定示例中,机组感染后有一天可以居住。因此,如果机组人员前往第1行星,则它们会被感染,然后在下一个行星(4或6)上死亡。如果他们去了第2行星,他们就被感染了,他们只有一天的生命,所以下一颗行星就死了……结果就是路径0 3 10 11 12 5 13。
行星与已知宇宙的另一个例子。起始行星为0,终止行星为9。如您所见,行星4、6、7被感染。在此特定示例中,机组感染后有两天的时间。因此,如果机组人员前往第4行星,则它们会被感染,然后在两天后(一天=从一个行星到另一个行星的一种方式)死亡。结果是路径0 1 2 3 5 7 6 9或0 1 2 3 5 7 89。如您所见,在某些情况下,可能会有更多路径。任务是只找到一个,而不必是最短的一个。乘员组从头到尾的正确路线。
我曾经使用过命令式DFS算法,但是我不知道如何实现这样的功能:如果机组人员将死在一条路径上,那么该算法应该找出另一条路径。您可以从图表图片中获取它。
解决方法
您可以进行修改的BFS来解决此问题。在执行BFS时,应将感染的天数保持为状态的一部分。然后,在遍历时,如果我们要以更好的感染天数(即更少)来遍历,则接受遍历到先前访问的节点。
这也是最短路径,因为BFS将在非加权图中产生最短路径。
这是Python的快速草图,适用于第二个示例:
from collections import deque
def bfs(graph,virus_nodes,start,target,survive_days):
survive_days = min(survive_days,len(graph))
visited = {start : 0}
queue = deque([(start,(start,))]) # node,days infected,path
while queue:
node,days_infected,path = queue.popleft()
is_infected = days_infected > 0 or node in virus_nodes
nxt_days_infected = days_infected + 1 if is_infected else 0
if nxt_days_infected > survive_days:
continue
for nxt in graph[node]:
if nxt == target:
return path + (target,)
if nxt not in visited or visited[nxt] > nxt_days_infected:
queue.append((nxt,nxt_days_infected,path + (nxt,)))
visited[nxt] = nxt_days_infected
if __name__ == "__main__":
graph = {
0 : [1],1 : [0,2],2 : [1,3],3 : [2,4,5],4 : [3,7],5 : [3,6 : [7,9],7 : [4,5,6,8],8 : [7,9 : [6,}
virus_nodes = {4,7}
max_survive_days = 2
print(bfs(graph,9,max_survive_days))
输出:
(0,1,2,3,7,9)
,
解决此问题的最简单方法是使用BFS,但是您要从结束开始。
让我们说感染后的生存时间为TTL天。
从目标位置运行BFS,但是您只能考虑在小于TTL长的路径上进入被感染的行星。一旦BFS碰到了一条TTL长度的路径,那么您就只能从此一直使用干净的行星,一直到开始。
如果逐级执行BFS,这特别容易。然后,您只需要检查
如果您不在意这是不是最短的路径,则可以使用DFS并进行一些调整,以合并消亡时间。 JSFiddle和javascript代码:
function findPath(path,infectedNodes,nodesBeforeDeath,initialNode,targetNode,adjacentTable) {
const lastNode = path.visitedNodes[path.visitedNodes.length - 1];
if (lastNode === targetNode) {
return path;
}
if (path.nodesLeftBeforeDeath !== null && path.nodesLeftBeforeDeath !== undefined && path.nodesLeftBeforeDeath == 0) {
return;
}
const adjacentNodes = adjacentTable[lastNode];
for (const node of adjacentNodes) {
if (path.visitedNodes.indexOf(node) > -1) {
continue;
}
const newPath = {
visitedNodes: [...path.visitedNodes,node]
};
if (path.nodesLeftBeforeDeath !== null && path.nodesLeftBeforeDeath !== undefined) {
newPath.nodesLeftBeforeDeath = path.nodesLeftBeforeDeath - 1;
}
else if (infectedNodes.indexOf(node) > - 1) {
newPath.nodesLeftBeforeDeath = nodesBeforeDeath;
}
const nodeResult = findPath(newPath,adjacentTable);
if (nodeResult) {
return nodeResult;
}
}
}
const firstExampleResult = findPath({
visitedNodes: [0]
},[1,4],13,[[1,[0,6],10],11,12],[6,12,13],[2,14],[3,11],[4,10,[5],[7,8]]
);
console.log(firstExampleResult.visitedNodes);
const secondExampleResult = findPath({
visitedNodes: [0]
},[
[1],8]
]
);
console.log(secondExampleResult.visitedNodes);
,
您可以使用Dijkstra's algorithm之类的最短路径算法来查找两个节点之间的最短路径。
边缘权重就是您可能要去的节点的权重。在这种情况下,去往非病毒节点的所有边缘的权重为1
。当边缘到达病毒节点时,权重为number of nodes + 1
。拜访任何病毒行星都比拜访其他所有行星要容易得多。这样,只有在没有其他选择的情况下才选择病毒行星。
它还处理了必须访问多个病毒星球的情况,方法是选择病毒星球数量最少的路径,而不是在这种情况下确实很重要。虽然如果要列出所有同样有效的解决方案,则可以考虑将任何其他病毒星球的权重设置为1(如果路径中已经包含病毒星球)。如果重量与最佳解决方案相同,则将其显示为另一种有效解决方案。
关于生存时间,我们将其称为T
,只需检查当前路径即可。如果搜索中的当前节点不是结束节点,并且自第一个病毒行星越过以来,它就是T
个行星。然后拒绝该路径为无效。
生存时间的另一种处理方法是使用数组权重[path weight,virus age]
。这意味着,在相同的路径权重下,它将以最低的病毒年龄扩展路径。病毒年龄仅为# of days since exposed to virus
。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。