有一个博物馆组织为 NxN 房间。部分房间上锁且无法进入。其他房间是开放的,有些房间有警卫。守卫只能通过开放的房间和博物馆内的南北东和西移动。对于每个房间,找出到警卫的最短距离。你的算法的时间复杂度是多少?
8 回答
首先,考虑一种相对幼稚的方法。
将每个守卫 G 作为房间集合中的一个顶点,R=N*N,以及相邻房间(边)E 之间的可能路径。
如果必须知道所有房间的最小距离,则从每个警卫到每个房间的 BFS。
这应该需要时间G * (R+E)
,或者类似G*(N*N+E)
,或者G*(N*N)
。
这当然可以通过记忆来优化,因为每个 BFS 将重复计算已经完成的子树。根据房间配置,这可以大大降低上述时间复杂度的 G。但是,我确信一定有比这更好的东西。
图形解决方案 每个房间都是一个节点 只有在门打开的房间之间才有边,使用 Floyd–Warshall 算法,然后检查每个房间和每个警卫之间的最小距离
房间数量 - R = N^2
守卫人数 - G
时间复杂度为 O(R^3 +R*G)= O(R^3) = O(N^6)
弗洛伊德-沃歇尔的 R^3
如果我们用人工源和从源到每个警卫的零长度边来扩充博物馆图,那么单源最短路径树,可通过 BFS(未加权)/Dijkstra(加权)在时间Õ(n 2 + g ),给出从每个房间到最近警卫的距离。
这是IVlad和throwawayacct提到的最佳解决方案 ( O(N^2) ) 的摘要。出于某种原因,他们的声音没有被听到:)
将 NxN 网格视为一个图形,其中节点代表房间,边代表相邻房间之间的门。所有边的权重为 1。一组节点 U 被标记为“守卫房间”。
这个想法是使用 BFS 遍历,它从连接到所有受保护房间的新节点开始,并从 v0 开始按距离递增的顺序扫描房间。每次访问一个节点时,到达该节点所走的步数代表了到一个被看守的房间的距离,并且由于路径扩展顺序,它保证是最小的:
Add an artificial node v0 and connect it to all nodes in U
Create a simple queue Q, that stores pairs <node, dist>
Q.enqueue(<v0, -1>)
Create a hash set S, that contains all visited nodes
S.add(v0)
while not Q.isEmpty() {
pathTail := Q.dequeue()
output distance pathTail.dist for node pathTail.node
for all neighbours v of pathTail.node
if not S.contains(v)
Q.add(<v, pathTail.dist + 1>)
S.add(pathTail.node)
}
复杂性分析
请注意,该图是平面的,所以我们有 |V|+|E|=O(|V|)。因此,这个 BFS 在 O(|V|)=O(N^2) 时间内运行。
我们对边有统一的权重这一事实使事情变得简单;保证队列具有单调的距离值。以 dijkstra 为例,边的权重可能不同,因此需要优先级队列,这会影响时间复杂度。这里提出的相同问题,房间之间的权重不同,需要 O(N N log N) 时间。
您可以在本系列文章中找到 Dijkstra 的最短路径算法:
我假设一个函数 edgeCost(i,j) 返回从房间 i 到房间 j 的转换成本(如果没有,则为无限,否则为 1)。edgeCost(i,i) = 0 并且没有负循环。设 R 为房间数(在您的情况下,R = N^2):
int path[R][R];
/*
* Each path[i][j] is initialized to
* edgeCost(i,j) or infinity if there is no
* edge between i and j.
*/
function play():
for k = 1 to R:
for i = 1 to R:
for j = 1 to R:
path[i][j] = min(path[i][j], path[i][k] + path[k][j]);
或者您可能知道,Floyd-Warshall 算法(所有对最短路径),时间复杂度为 O(|R|^3)。
有一个队列,每个条目都包含一个正方形的地址和一个整数。将所有守卫的位置放在一个队列中,每个都用整数“0”标记。每个方格都应该有一个数字的空间,并且它们最初都应该是空白的。
然后,只要队列中有东西,就从队列中拉出一个条目,并检查有问题的方块是否有标记值。如果是这样,只需忽略该队列条目并继续下一个。否则,用队列中的值标记方格,并将所有可直接到达的相邻方格放入队列中,其值比当前当前条目的值高一个(以便从队列中拉出第一批方格,他们将获得值“1”)。每个方块在空白时只会被访问一次,并且每个被访问的方块最多会导致四个更多的方块被排队,因此通过队列的物品总数将是方块数量的四倍,再加上守卫的数量. 通过在将每个正方形添加到队列之前检查它是否为空白,可以轻松地将这个值减少一个常数因子;这不会消除所有冗余排队,但会消除一些。