❄️ | 题目描述
模拟一个电路的实际运行情况。
一个电路中有 m 个输入以及 n 个功能器件。在这些器件之间有线路相连,一个功能器件可能有多个输入。
这构成了一个有向图。
如果图中有环,则输出 “LOOP” 并结束
否则则模拟电路运行 S 次。每次给出 m 个输入以及 cnt 个需要输出器件值的编号(无序),需要按照编号顺序输出电路运行后这些器件的输出值。
每个检测点有多组数据,t 组。
✨ | 实现思路
拓扑排序 + 大模拟
首先根据题目的输入建图。将输入以及功能器件都看成节点,连接的电路看成有向边。
根据拓扑排序判断图是否有环。
没环的话,根据拓扑排序的顺序遍历每一个节点,模拟电路运行情况。
这里要提前保存下来输入以及要输出的器件编号。在每次运行后根据编号输出。
💓 | 具体代码
```cpp
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
const int N = 3010; // 输入 + 器件数量最多N + kN = 3000个
const int M = N * 5; // 每个点最多 k = 5 个输入
int m, n; // 输入数量、器件数量
int h[N], e[M], ne[M], idx;
int w[N]; // 第 i 个器件的值 [1 - m的输入器件 m+1 - n的功能器件]
int f[N]; // 第 i 个器件的功能
vector<int> in[M], out[M]; // 第 i 次运行的所有输入、所有要输出的器件编号
int q[N], d[N]; // 拓扑排序队列、入度
void add(int a, int b) // 添加一条边a->b
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
d[b] ++; // b的入度 ++
}
int get(char *str)
{
string name[] = {"AND", "OR", "NOT", "XOR", "NAND", "NOR"}; // 1, 0, 0, 0, 1, 0
for (int i = 0; i < 6; i ++)
if (name[i] == str) return i;
return -1;
}
bool topsort()
{
int hh = 0, rr = -1;
for (int i = 1; i <= m + n; i ++)
if (d[i] == 0) q[++ rr] = i;
while (hh <= rr)
{
int u = q[hh ++];
for (int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if (-- d[j] == 0) q[++ rr] = j;
}
}
return rr == m + n - 1;
}
int main()
{
int T;
cin >> T;
while (T --)
{
memset(h, -1, sizeof h);
idx = 0;
memset(d, 0, sizeof d);
memset(q, 0, sizeof q);
cin >> m >> n;
char str[100];
int cnt;
for (int i = 1; i <= n; i ++)
{
cin >> str >> cnt;
f[m + i] = get(str);
while (cnt --)
{
cin >> str;
int t = atoi(str + 1);
if (str[0] == 'I') add(t, m + i);
else add(m + t, m + i);
/*
图中共 m + n 个节点,前m个是输入
Im:表示第 m 个输入信号连接到此输入端,保证 1≤m≤M;
On:表示第 n 个器件的输出连接到此输入端,保证 1≤n≤N。
*/
}
}
int S;
cin >> S;
for (int i = 1; i <= S; i ++)
{
in[i].clear();
for (int j = 1; j <= m; j ++)
{
int x;
cin >> x;
in[i].push_back(x);
}
}
for (int i = 1; i <= S; i ++)
{
out[i].clear();
int cnt; cin >> cnt;
while (cnt --)
{
int x; cin >> x;
out[i].push_back(x);
}
}
if (!topsort())
{
puts("LOOP");
continue;
}
for (int k = 1; k <= S; k ++) // 运行S次
{
// 初始化输入
for (int i = 1; i <= m; i ++)
w[i] = in[k][i - 1];
// 每次运行前初始化器件值,保证不影响计算
for (int i = m + 1; i <= m + n; i ++)
{
if (f[i] == 0 || f[i] == 5) w[i] = 1;
else w[i] = 0;
}
for (int i = 0; i < m + n; i ++)
{
int u = q[i], t = w[u];
// 枚举通向的下一个器件
for (int j = h[u]; j != -1; j = ne[j])
{
// "AND", "OR", "NOT", "XOR", "NAND", "NOR"
int v = e[j];
if (f[v] == 0) w[v] &= t;
else if (f[v] == 1) w[v] |= t;
else if (f[v] == 2) w[v] = !t;
else if (f[v] == 3) w[v] ^= t;
else if (f[v] == 4) w[v] |= !t;
else if (f[v] == 5) w[v] &= !t;
}
}
// 输出。注意输出根据的是功能器件编号,而建图时节点不是1-n,而是 m+1 - m+n
// 所以这里输出时要加上 m
for (auto x : out[k])
{
cout << w[m + x] << ' ';
}
puts("");
}
}
}
```