📅  最后修改于: 2023-12-03 14:58:34.089000             🧑  作者: Mango
本题是 Sudo GATE 2020 Mock I(2019 年 12 月 27 日)的第 30 题,题目名称为“门”。
题目描述如下:
给定一个由 n
个节点和 m
条边组成的无向图。每个节点都有一个初始状态,为 0
或 1
。每条边的开门条件为两端节点的状态必须不同,即一个为 0
另一个为 1
。当开门后,两端节点的状态会互相交换。你的任务是找出最少需要开几次门,才能使得所有节点的状态相同。
编写一个函数 minimumDoors(n: int, entrances: List[int], exits: List[int], path: List[List[int]]) -> int
,其中:
n
:节点数量,$1 \leq n \leq 500$。entrances
:入口节点的编号组成的列表,$1 \leq |entrances| \leq n$。exits
:出口节点的编号组成的列表,$1 \leq |exits| \leq n$。path
:一个长度为 m
的列表,其中 path[i]
表示边 i
的两个端点的编号,$0 \leq path[i][0], path[i][1] \leq n-1$,且保证连接的是不同的两个节点。这道题可以使用网络流算法解决,具体来说,可以将每个门看做一个边,即对于某条边 $(u, v)$,如果 $u$ 的状态为 $0$,$v$ 的状态为 $1$,则可以将这条边看作从 $u$ 到 $v$ 的一条容量为 $1$ 的边;如果 $u$ 的状态为 $1$,$v$ 的状态为 $0$,则可以将这条边看作从 $v$ 到 $u$ 的一条容量为 $1$ 的边。
然后,我们可以从入口节点向所有门连一条容量为 $1$ 的边,所有门向出口节点连一条容量为 $1$ 的边。
最后,对于这个网络流图,我们可以使用 Dinic 算法求最大流,流量即为开门次数。
时间复杂度 $O(n^2m)$。
以下是 Python 3 代码实现,注释中标明了每个变量的含义或作用:
from collections import defaultdict
from typing import List
def minimumDoors(n: int, entrances: List[int], exits: List[int], path: List[List[int]]) -> int:
# 网络流算法求解最小开门次数
# 首先建立从源点连接所有入口节点的边
# 然后将所有门看作一个边,u 的状态为 0,v 的状态为 1,则从 u 向 v 连一条容量为 1 的边
# 对于 u 的状态为 1,v 的状态为 0 的情况,则从 v 向 u 连一条容量为 1 的边
# 最后,从所有门向汇点连接容量为 1 的边
# 用 Dinic 算法求解最大流,最大流即为最小开门次数
edges = defaultdict(list) # 存放边的连接情况
for u in entrances: # 从源点向所有入口节点连接边
edges["S"].append((u, 1, 0))
edges[u].append(("S", 0, 0))
for v in exits: # 从所有出口节点向汇点连接边
edges[v].append(("T", 1, 0))
edges["T"].append((v, 0, 0))
for u, v in path: # 处理所有门的边
edges[u].append((v, 1 if u&1 else 0, 0)) # 如果 u 表示的状态为 0,向 v 连容量为 1 的边,否则容量为 0
edges[v].append((u, 1 if v&1 else 0, 0)) # 如果 v 表示的状态为 0,向 u 连容量为 1 的边,否则容量为 0
def bfs(src, dst, path):
queue = [src]
visited = {src}
while queue:
u = queue.pop(0)
for v, cap, flow in edges[u]:
if cap - flow > 0 and v not in visited:
visited.add(v)
path[v] = (u, cap - flow)
if v == dst:
return True
queue.append(v)
return False
def dinic(src, dst):
max_flow = 0
while True:
path = {}
if not bfs(src, dst, path):
break
v = dst
delta = float("inf")
while v != src:
u, d = path[v]
delta = min(delta, d)
v = u
max_flow += delta
v = dst
while v != src:
u, d = path[v]
for i, (t, cap, flow) in enumerate(edges[u]):
if t == v:
edges[u][i] = (t, cap, flow + delta)
break
for i, (t, cap, flow) in enumerate(edges[v]):
if t == u:
edges[v][i] = (t, cap, flow - delta)
break
v = u
return max_flow
return dinic("S", "T")