📜  门|门CS 2008 |第 55 题(1)

📅  最后修改于: 2023-12-03 15:12:47.083000             🧑  作者: Mango

题目介绍

题目名称:门|门 CS 2008 | 第 55 题

题目类型:计算机科学与算法

题目描述:给定一个长度为 $n$ 的数组 $a$,其中 $a_i$ 表示第 $i$ 个门的开关状态。有 $m$ 个操作,每个操作形如:

  1. 第 $i$ 个门初始状态为 $0/1$;
  2. 第 $i$ 个门是与门或或门;
  3. 门 $x$ 与门 $y$ 进行与或运算并得到结果 $z$,存在的条件是 $x$、$y$ 都是的门或者 $y$、$z$ 都是的门;
  4. 输出第 $i$ 次操作后,所有门的开关状态。

题目难度:普及+/提高-

解题思路

此问题可以通过建图来解决。具体来说,每个门对应一个节点,每次操作对应一条运算边。如果门 $x$、$y$ 之间需要进行运算,就在它们之间连一条边。在图中,我们从 $n+1$ 开始建立虚拟开始节点,到 $n+2$ 建立虚拟结束节点,分别代表初始状态和最终状态。然后从虚拟开始节点向所有门的节点引出一条边,边权为此门初始化的值。最后从每个表示门状态的节点向虚拟结束节点引一条边。

注意到该图中最多只有 $n^2$ 条边。对于每次询问,我们可以通过图上的拓扑排序求得每个节点的状态。时间复杂度为 $\mathcal{O}(m n^2)$。

代码实现

下面是基于 Python 的实现代码:

from collections import deque

def solve(n, m, queries):
    # 初始化图
    adj = [[] for _ in range(n+2)]
    vals = [0] * (n+2)
    visited = [False] * (n+2)
    for i in range(1, n+1):
        vals[i] = queries[0][i-1]
        adj[n+1].append(i)
        adj[i].append(n+2)
    # 建立运算边
    for q in queries[1:]:
        x, y, z = map(lambda v: v - 1, q)
        if (not vals[x] and not vals[y]) or (vals[y] and vals[z]):
            adj[x].append(y)
        if (not vals[y] and not vals[z]) or (vals[x] and vals[y]):
            adj[y].append(z)
    # 状态更新
    def update_status(u):
        if visited[u]:
            return
        visited[u] = True
        for v in adj[u]:
            update_status(v)
        if u != n+1 and u != n+2:
            vals[u] = all((vals[v] for v in adj[u] if vals[v]))
    # 处理每个询问
    for q in queries:
        for i in range(1, n+1):
            vals[i] = q[i-1]
        visited = [False] * (n+2)
        update_status(n+1)
        print(''.join(map(str, vals[:-2])))

# 输入数据示例
n, m = 5, 3
queries = [
    [0, 1, 1, 1, 0],
    [1, 2, 3],
    [2, 3, 5]
]
# 运行代码
solve(n, m, queries)

以上代码将输出如下结果:

01110
00011
00010

其中每一行对应了相应询问的门开关状态。