📜  门|门CS 2010 |第 42 题(1)

📅  最后修改于: 2023-12-03 14:58:36.969000             🧑  作者: Mango

题目介绍:门|门CS 2010 |第 42 题

题目描述:

有 $N$ 个门,每个门都是由一个个人来开的,但他只能开某些门。对于每个人,他可以开若干个门。

现在有 $M$ 个人排成了一排,每个人可以选择开一扇门或者不开门。问有多少种方案可以使得所有的门都被打开至少一次。

输入格式

第一行两个整数 $N,M$。

接下来 $M$ 行,每行第一个数字 $k_i$,表示这个人可以打开的门的个数,后面跟 $k_i$ 个数字,表示这个人能够打开的共 $k_i$ 扇门的编号。

输出格式

输出一个整数表示方案数。

输入样例1:
3 2
2 1 3
1 2
输出样例1:
2
输入样例2:
3 3
1 1
1 2
1 3
输出样例2:
1
题解

本题为一个搜索问题,首先可以想到使用回溯法。

定义一个集合 set 记录当前已经开了的门的编号,分别求出每个人能够打开的门的编号的并集,判断当前这个并集是否是全集(也就是所有门都被开了一次),如果是全集就将全局方案数 $res$ 增加 $1$,否则对这个人可以打开的门的编号遍历一遍,如果这个门还没有被开过,就递归进去。

最后返回答案即可。

代码实现
def backtrack(n, m, graph, cur, res, vis):
    if set(range(1, n + 1)) == cur:
        res[0] += 1
        return
    if m == 0:
        return
    idx, doors = graph[m - 1]
    for door in doors:
        if not vis[door]:
            vis[door] = True
            backtrack(n, m - 1, graph, cur.union({door}), res, vis)
            vis[door] = False

def main():
    n, m = map(int, input().split())
    graph = []
    for i in range(m):
        lst = list(map(int, input().split()))
        graph.append((lst[0], lst[1:]))
    res = [0]
    vis = [False] * (n + 1)
    backtrack(n, m, graph, set(), res, vis)
    print(res[0])

if __name__ == '__main__':
    main()

代码片段: