📅  最后修改于: 2023-12-03 14:58:36.969000             🧑  作者: Mango
有 $N$ 个门,每个门都是由一个个人来开的,但他只能开某些门。对于每个人,他可以开若干个门。
现在有 $M$ 个人排成了一排,每个人可以选择开一扇门或者不开门。问有多少种方案可以使得所有的门都被打开至少一次。
第一行两个整数 $N,M$。
接下来 $M$ 行,每行第一个数字 $k_i$,表示这个人可以打开的门的个数,后面跟 $k_i$ 个数字,表示这个人能够打开的共 $k_i$ 扇门的编号。
输出一个整数表示方案数。
3 2
2 1 3
1 2
2
3 3
1 1
1 2
1 3
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()
代码片段: