📅  最后修改于: 2023-12-03 15:12:47.543000             🧑  作者: Mango
该问题属于Codeforces平台的比赛题目,旨在考察参赛者的编程能力和算法能力。该问题的难度为1600,属于中等难度。
有 $n$ 扇门,编号从 $1$ 到 $n$。每扇门有若干把锁,只有所有锁都被打开,门才能被打开。共有 $m$ 把钥匙,编号从 $1$ 到 $m$。每把钥匙只能打开其中的一扇门上的一个锁。
现在给出 $k$ 个人的想要开哪些门,第 $i$ 个人的想法为:打开编号为 $a_{i,1},a_{i,2},...,a_{i,x_i}$ 的门。每个人都有无限个钥匙,但是每个人只能打开他自己想要打开的门。
试问最少需要多少钥匙才能使得所有人都能打开他们想要打开的门。
输入共有 $k+1$ 行,第一行包含三个整数 $n,m,k$,表示门的数量、钥匙的数量和人的数量。
接下来 $k$ 行,每行包含若干个整数,用空格隔开。第 $i$ 行的 $x_i$ 表示第 $i$ 个人有 $x_i$ 扇门想要打开,接下来 $x_i$ 个数字表示第 $i$ 个人所有想要打开的门的编号。
输出一个整数,表示最少需要多少钥匙才能使得所有人都能打开他们想要打开的门。
3 6 2
2 2 3
2 1 2
2
6 9 3
3 1 4 6
3 2 4 6
3 3 4 6
3
本题需要使用最小割算法来解决。我们将每把钥匙抽象为图中的一条边,每扇门看作是一个节点,每个人需要打开的门看作是一条源点到门节点的有向边。同时,我们也将源点和汇点加入到图中,源点到每个人对应的节点有一条有向边,汇点从每扇门的节点出发有一条有向边。
接下来,我们对这个图求最小割即可。最小割的值即为最少需要的钥匙数。
from collections import defaultdict
class Dinic:
INF = 1 << 30
def __init__(self, n):
self.n = n
self.edges = defaultdict(list)
self.level = [-1] * (n + 1)
self.ptr = [0] * (n + 1)
def add_edge(self, u, v, c):
self.edges[u].append({"dest": v, "capacity": c, "next": len(self.edges[v])})
self.edges[v].append({"dest": u, "capacity": 0, "next": len(self.edges[u]) - 1})
def bfs(self, s, t):
self.level = [-1] * (self.n + 1)
self.level[s] = 0
q = [s]
while q:
u = q.pop(0)
for e in self.edges[u]:
v = e["dest"]
if self.level[v] == -1 and e["capacity"] > 0:
self.level[v] = self.level[u] + 1
q.append(v)
return self.level[t] != -1
def dfs(self, u, t, f):
if u == t:
return f
for i in range(self.ptr[u], len(self.edges[u])):
e = self.edges[u][i]
v = e["dest"]
if e["capacity"] > 0 and self.level[v] == self.level[u] + 1:
incf = self.dfs(v, t, min(f, e["capacity"]))
if incf:
self.edges[u][i]["capacity"] -= incf
self.edges[v][e["next"]]["capacity"] += incf
return incf
return 0
def max_flow(self, s, t):
max_flow = 0
while self.bfs(s, t):
self.ptr = [0] * (self.n + 1)
while True:
incf = self.dfs(s, t, self.INF)
if not incf:
break
max_flow += incf
return max_flow
n, m, k = map(int, input().split())
S = n + m + k + 1
T = n + m + k + 2
network_flow = Dinic(n + m + k + 2)
for i in range(1, k + 1):
ableDoors = set(map(int, input().split()[1:]))
network_flow.add_edge(S, i, 1)
for j in ableDoors:
network_flow.add_edge(i, k + j, 1)
for i in range(1, n + 1):
network_flow.add_edge(k + i, T, 1)
for i in range(1, m + 1):
network_flow.add_edge(S, k + i, 1)
print(network_flow.max_flow(S, T))
解释:
完整解法代码可以从我的GitHub获取。