📜  门|门 CS 1996 |第 59 题(1)

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

门|门 CS 1996 |第 59 题

这是门|门 CS 1996 的第 59 题,本题为算法题。

题目描述:

有 $n$ 个门和 $m$ 把钥匙,每把钥匙都可以打开一些门。钥匙可以重复使用,每个门只能使用一次钥匙。现在你要从门 0 出发,到达门 $n-1$。求最少使用多少把钥匙。

示例:

n = 5
m = 6
doors = [[0, 1], [0, 3], [1, 2], [1, 3], [2, 4], [3, 4]]
keys = [[1, 2, 3], [0, 1], [2], [4], [3]]
print(min_keys(n, m, doors, keys))  # 输出 2 

代码实现:

def min_keys(n, m, doors, keys):
    # 定义队列
    q = []
    # 定义 visited 数组
    visited = [False] * n
    
    # 初始状态,从门 0 出发,使用 0 把钥匙
    status = (0, 0, [])
    # 将初始状态加入队列
    q.append(status)
    
    while q:
        # 取出队列头
        cur = q.pop(0)
        # 如果当前状态到达终点,返回使用的钥匙数
        if cur[0] == n - 1:
            return cur[1]
        # 如果当前状态已经访问过,直接跳过
        if visited[cur[0]]:
            continue
        # 标记当前状态为已访问
        visited[cur[0]] = True
        # 搜索可达状态
        for door in doors:
            # 如果门已经被打开,跳过
            if door[0] == cur[0] and door[1] in cur[2]:
                continue
            # 如果门还未被打开,查找能否打开门的钥匙
            if door[0] == cur[0]:
                for key in keys[door[1]]:
                    # 如果找到钥匙,构造新状态并加入队列
                    if key not in cur[2]:
                        new_keys = cur[2] + [key]
                        new_status = (door[1], cur[1] + 1, new_keys)
                        q.append(new_status)
    # 如果搜索完毕没有找到终点,返回 -1
    return -1

注释详解:

1.首先定义一个队列 q,用于存储待搜索的状态。

2.定义一个 visited 数组,记录当前状态是否已经访问过。

3.定义初始状态 status,从门 0 出发,使用 0 把钥匙。将初始状态加入队列。

4.使用 while 循环不断从队列 q 中取出状态并进行搜索。如果队列为空,停止搜索。

5.取出队列头 cur,如果当前状态到达终点,返回使用的钥匙数。

6.如果当前状态已经访问过,直接跳过。

7.标记当前状态为已访问。

8.搜索可达状态。对于每个门,如果门已经被打开,跳过;如果门还未被打开,查找能否打开门的钥匙。如果找到钥匙,构造新状态并加入队列。

9.如果搜索完毕没有找到终点,返回 -1。

代码时间复杂度为 $O(m^2 \log m)$,其中 $m$ 为钥匙的数量。这是因为我们需要对每个门都遍历一遍所有的钥匙,查找能否打开门,时间复杂度为 $O(m \log m)$,总时间复杂度为 $O(m^2 \log m)$。