📜  门| GATE CS 2020 |问题26(1)

📅  最后修改于: 2023-12-03 15:28:38.674000             🧑  作者: Mango

门 | GATE CS 2020 | 问题26

问题描述

一个教室里有 $N$ 个座位排成一排,在第 $i$ 个座位上坐着一个学生,$1 ≤ i ≤ N$。教室一共有 $M$ 扇门,第 $j$ 扇门防止在第 $a_j$ 到第 $b_j$ 的座位之间出现交通堵塞。两个座位之间的距离为 $1$,如果一个学生从座位 $i$ 移动到座位 $k$($k > i$),那么他需要走 $k - i$ 步。

现在,有一个学生从第 $S$ 个座位出发,希望到达第 $T$ 个座位。学生行动的顺序如下:

  1. 如果当前座位与 $T$ 的距离小于等于当前座位与 $S$ 的距离,学生会移动到 $T$ 所在的座位并停止行动。
  2. 否则,学生会移动到与当前座位的距离最小的 $A$ 座位,满足 $S < A ≤ T$,且能够到达 $A$ 座位。
  3. 如果满足条件的座位 $A$ 不存在,则学生无法到达 $T$。

给定 $N$,$M$,$S$,$T$ 和 $M$ 扇门的信息,请实现如下函数:

def min_distance(n: int, m: int, S: int, T: int, edges: List[Tuple[int, int]]) -> int:
    pass
输入格式

输入的第一行包含五个用空格分隔的整数 $N,m,S,T$($2 ≤ N ≤ 10^5,1 ≤ m ≤ 1000,1 ≤ S,T ≤ N$)。

接下来 $m$ 行,每行包含两个用空格分隔的整数 $a_j$ 和 $b_j$($1 ≤ a_j$ ≤ $b_j ≤ n$),表示第 $j$ 扇门防止学生在 $a_j$ 到 $b_j$ 之间出现交通堵塞。

输出格式

输出一个整数,表示从 $S$ 移动到 $T$ 的最短距离。

示例

输入样例:

6 1 3 6
3 5

输出样例:

3
解题思路

这里给出两种解题思路。

思路一

我们可以使用 BFS 来在有障碍物的坐标系中寻找到达终点的最短距离。

对于有障碍物的坐标系,我们可以把每个障碍物看作是图中的一个节点,只有在两个障碍物不发生碰撞的情况下才能够从一个障碍物到达另一个障碍物。利用这个性质,我们可以将整个坐标系转化为一个无向图。

可以考虑将每个座位抽象为节点,将两个座位之间的距离设为边权值。为了避免门之间的冲突,我们可以将门可以连接的座位之间的边全部去掉。这样做之后,这个图就变成了一张稠密图,BFS 的复杂度可以达到 $O(N^{2})$。如果两个座位之间是不能到达的,则不需要将它们之间连通。

使用 BFS 寻找到起点到终点的最短路,但是不能直接使用 BFS。在这个坐标系中,源点 $S$ 可能无法直接到达目标点 $T$,因此,我们需要针对障碍物的图进行更改,使得 BFS 可以找到源点到目标点之间的最短路径。具体操作是:找到一个与 $T$ 最近且可以到达 $T$ 的障碍位置 $A$,这个障碍可以更新边的权值,把从 $A$ 到 $T$ 的所有边权改为 $1$。现在我们就可以直接使用 BFS 找到最短路。

代码示例:

from collections import deque
from typing import List, Tuple


def min_distance(n: int, m: int, S: int, T: int, edges: List[Tuple[int, int]]) -> int:
    graph = [[] for _ in range(n + 1)]
    for i in range(1, n):
        graph[i].append((i + 1, 1))
        graph[i + 1].append((i, 1))

    for i in range(m):
        a, b = edges[i]
        for j in range(a, b):
            graph[j].remove((j + 1, 1))
            graph[j + 1].remove((j, 1))

    visited = [False] * (n + 1)
    queue = deque([(S, 0)])
    visited[S] = True
    result = float('inf')

    while queue:
        current, distance = queue.popleft()
        if current == T:
            result = min(result, distance)
            continue

        for neighbor, weight in graph[current]:
            if not visited[neighbor]:
                queue.append((neighbor, distance + weight))
                visited[neighbor] = True

    return result
思路二

我们可以使用 Dijkstra 算法来寻找到达终点的最短距离。

使用一个堆维护到起点的距离,每次按照距离从小到大从堆里取出一个点,遍历它到达的点,如果这个点还没有更新过,就更新它到起点的距离,并把它加入堆中。在更新到达一个障碍物的节点时,需要在堆中插入到达最近且可以到达目标点的障碍物的这个点。如果没有找到一个可达节点,则学生无法到达目标点。

代码示例:

from heapq import heappush, heappop
from typing import List, Tuple


def min_distance(n: int, m: int, S: int, T: int, edges: List[Tuple[int, int]]) -> int:
    graph = [[] for _ in range(n + 1)]
    for i in range(1, n):
        graph[i].append((i + 1, 1))
        graph[i + 1].append((i, 1))

    for i in range(m):
        a, b = edges[i]
        for j in range(a, b):
            graph[j].remove((j + 1, 1))
            graph[j + 1].remove((j, 1))

    heap = [(0, S)]
    distance = [float('inf')] * (n + 1)
    distance[S] = 0

    while heap:
        current_distance, current = heappop(heap)
        if current_distance > distance[current]:
            continue

        if current == T:
            return current_distance

        for neighbor, weight in graph[current]:
            new_distance = current_distance + weight
            if new_distance < distance[neighbor]:
                distance[neighbor] = new_distance
                heappush(heap, (new_distance, neighbor))

    return -1
总结

这道题考虑比较重要的是障碍物间的处理,这里我们使用了两种方法:BFS 和 Dijkstra。BFS 空间复杂度较高,但是实现起来比较简单。Dijkstra 空间复杂度较低,但是可能需要更长的时间来运行。