📌  相关文章
📜  门| Sudo GATE 2020 Mock I(2019 年 12 月 27 日)|第 30 题(1)

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

题目介绍

本题是 Sudo GATE 2020 Mock I(2019 年 12 月 27 日)的第 30 题,题目名称为“门”。

题目描述如下:

给定一个由 n 个节点和 m 条边组成的无向图。每个节点都有一个初始状态,为 01。每条边的开门条件为两端节点的状态必须不同,即一个为 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")

参考文献