📜  门|门CS 2013 |第 32 题(1)

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

门|门CS 2013 |第 32 题

题目描述

有 $n$ 个门和 $n$ 个钥匙,第 $i$ 个钥匙可以开第 $i$ 个门。现在你需要通过交换钥匙的方式使每个门都能够被对应的钥匙打开。具体地,你可以交换两个钥匙,但不能将钥匙交换到其他门上去。

需要注意的是,本题有多组数据,对于每组数据,只有一个钥匙不能打开对应的门。如果不存在任何一个钥匙都不能打开对应的门,则输出 -1。

输入:

输入的第一行包括一个单独的正整数 $T$,表示有 $T$ 组数据。对于每组数据,第一行包含一个整数 $n$,表示门和钥匙的个数。下一行包含 $n$ 个整数 $a_1,a_2,...,a_n$,其中 $a_i$ 表示钥匙 $i$ 能够打开的门的编号。数据保证钥匙恰好能够打开与之对应的门。

输出:

对于每组数据,如果存在一个钥匙不能打开对应的门,则输出 -1。否则输出需要交换的钥匙的数量。

示例

输入:

2
3
2 3 1
3
3 1 2

输出:

1
2
思路分析

首先,钥匙和门之间形成了一张图,可以通过建立邻接表存储之后进行深度优先搜索来寻找不在环上的情况。

因为题目给定的钥匙能够打开对应的门,所以不在环上的情况只有以下两种:

  • 一个钥匙能够打开另一个钥匙对应的门,而这个钥匙不能打开其对应的门;
  • 一个门可以被开启两次。

以上两种情况都只需要进行一次交换就可以解决。

注意,如果存在多条不在环上的链,我们只需要取其中的一条进行交换就可以使得所有链都变成环。

参考代码
from typing import List


def get_missing_key(graph: List[List[int]]) -> int:
    key_cnt = len(graph)

    # 寻找不在环上的情况
    visited = set()
    for i in range(key_cnt):
        if i not in visited:
            cur_path = [i]
            while graph[cur_path[-1]]:
                cur_path.append(graph[cur_path[-1]].pop())
            root = cur_path[0]
            visited.update(cur_path)
            if cur_path[-1] == root:
                cur_path.pop()
            if cur_path:
                missing_key = cur_path[0]
                if not graph[missing_key]:
                    for j in range(1, len(cur_path)):
                        if not graph[cur_path[j]]:
                            return 2
                    return 1
    return -1


def main():
    t = int(input())
    for _ in range(t):
        n = int(input())
        keys = list(map(int, input().split()))
        graph = [[] for _ in range(n)]
        for i in range(n):
            graph[i].append(keys[i] - 1)
        print(get_missing_key(graph))


if __name__ == '__main__':
    main()

解释:此题的Python代码已经封装成函数形式,本代码片段只是为了提供代码预览。